42

Node 的 “字节码增强技术” 你了解吗?

 4 years ago
source link: https://www.tuicool.com/articles/732e6bJ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

uiqqIz6.gif 戳蓝字「 Node全栈进阶 」关注我们哦!

EZVZZv7.jpg!web

编者注:作者是网易资深前端@金炳,当前负责网易严选自研Node框架建设工作。做过前端、后端、产品设计,是一名全栈工程师。目前致力于Node应用框架研究开发与生态建设,实践Node应用在Serverless、Faas场景下的迁移和落地,探索Service Mesh在Node应用中的价值。

Node的“字节码增强技术”你了解吗?纠正,专业名词叫monkey patch

一、介绍

1.1 什么是“字节码加强技术”?

有些人可能没有听过“字节码增强技术”是什么?

这个技术其实来源于Java,被一些牛逼的Java工程师掌握的核心技术之一,被用来开发一些底层的基建类工具或者平台使用。

“字节码增强技术”: 在Java字节码生成之后, 运行期 对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。Java字节码增强主要用来提供一些现有代码没有的功能,赋能原有项目,或者增加性能等。

1.2 类比Node

概括的讲:相当于我们用typescript或者javascript写的代码,编译成一个js文件或者打包后,使用了“字节码增强技术”之后,可以增强原有的代码或者修改底层的代码。例如原来写了一个node程序,通过加入这个技术之后,你可以将原有的node程序对接到分布式链路跟踪平台上,看到对应的链路情况,代码并没有改,但是你让一个简单的node项目有了链路跟踪的功能。那我把它也叫做Node的“字节码增强技术”了。

二、举个简单的例子

2.1 写个程序

我们为了文章的可读性,贴太多代码不好,所以举一个特别简单的代码:首先我们创建一个工程

// 创建一个工程目录
mkdir test_code 
// 进入工程目录
cd test_code
// 初始化package.json
npm init -y
// 安装koa的依赖
npm install koa -s

然后我们创建一个index.js

var koa = require('koa')

const app = new koa();

app.use((ctx, next)=>{
    ctx.body = "hello world";
})

app.listen(8000);

然后我们运行这个工程:

node index.js

然后我们就可以访问http://127.0.0.1:8000,然后就能在浏览器上看到"hello world"了。

2.2 使用“字节码增强技术”

假设上面的代码,就是我们的业务代码。

我们做基建的人,肯定最好的情况就是不去修改上面的代码。所以我们就来利用“字节码增强技术”来开发一个工具,来增强原来的代码。

我们这个工具名字:power.js

利用“字节码增强技术”,我们需要借助shimmer这个npm包(早先知道他的时候,下载还很少,现在越来越多人用了)。如下:每周下载量有105万了,说明项目底层可能都用了,大家放心使用。

N7Bf2uY.jpg!web image.png

我们安装一下shimmer这个依赖

npm install shimmer -S

然后power.js的代码如下:

我们比如想要增强,原先代码,我们不知道有哪些path被调用了,那我们就增强一下这个功能。

var shimmer = require('shimmer');
var http = require('http');

// 此处为了简单,http.createServer(handler)
shimmer.wrap(http, "createServer", function wrapCreateServer(original){
    return function wrapFunction (handler){
        let newFunction = function(req, res){
            console.log(req.url);
            return handler(req, res);
        }
        return original.call(this, newFunction);
    }
});

上面为了简单演示,就是我们会去加强原有http模块的createServer函数,作用就是将原有的handler做一个wrap。

相当于我们不管用koa还是express,我们底层都会去调用http.createServer方法。

所以我们通过加强http.createServer的handler方法。

然后我们怎么在应用:

node -r power.js index.js

我们通过-r power.js去引入这个文件

然后我们打开浏览器http://127.0.0.1:8000/hello,然后我们能看到,命令行里面会打印了

/hello

相当于打印出了这个url。

所以我们并没有去修改原有代码,而且这个插件,我们再使用一个express的应用程序,这个我体现在这个通用性这章节

2.3 通用性特点

为了验证这个通用性,我们在写一个express程序,

var express = require('express');
var app = express();

app.get('/*', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000);

然后我们也这个运行:

node -r power.js index.js

然后我们打开浏览器http://127.0.0.1:8000/hello,然后我们能看到,命令行里面会打印了

/hello

相当于打印出了这个url。

2.4 总结

当我们组的一些开发开发了一个应用,然后我们通过“字节码增强技术”开发了一个插件,然后让组内其他成员,运行应用的时候,去-r ./xxx,就能增强原来的代码了,比如有没有内存泄漏呀,链路跟踪,每次请求的返回啊,都能去做统计。

三、应用场景

3.1 基建类

上面这个我们举例了一个应用场景,比如其他成员开发了一个node业务项目,然后基建成员,可以去开发一个插件,让业务方成员启动的时候,加上这个参数。

最终实现:

  1. 内存泄漏统计

  2. 链路跟踪

  3. 每个请求的返回的耗时

  4. 错误捕获的上报

  5. mysql的调用情况的一些统计

  6. 等等

3.2 业务项目中的应用

上面我们演示的是node程序,他其实也能应用在前端代码中。

比如我们使用基建代码的时候,发现这个基建在某种情况下出问题了,这个时候我们就可以去做这个wrap操作。

否则等组件发包比较慢,这个时候,我们通过wrap原来的original函数,然后最终修复这个基建中出现的问题。

四、建议

上面的代码中演示的代码比较简陋,只是为了让大家能更好的理解。

4.1 插件包建议用typescript

因为我们wrap的时候,需要去看原有函数,而且原有函数可能有多种参数情况,所以我们typescript的时候,我们就能更好的看到这个情况。防止少覆盖了一些情况。

4.2 参考一些现有代码和官方文档

另外大家可以查看pandora.js这个项目里面这个的使用方式,以及另外github上面去搜索这个代码片段,就能看到一些基础类的代码中,都用了这个shimmer。

五、原理

像react、angular等,我们其实分析到底下,他最后就会落到那么几个最底层的原理。

那我们打开shimmer包。

打开源码地址:

https://github.com/othiym23/shimmer/blob/master/index.js

就一个文件,大家看一下就能看完了,最终这个包的最底层就是:

Object.defineProperty

如果听了之后,对你有所启发和帮助,希望能帮忙转发一下。如果有问题,请关注下面公众号,发送问题给我。

Mb26Bbf.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK