Node.js之async_hooks

Posted by 姚飞亮 on 2019-01-15

async_hooks,即异步钩子,看看这个API到底能帮我做些什么。

1. async_hooks基本用法

async_hooks API 能使我们追踪资源(resource)变得更加简单,只需要使用init、before、after、destroy这些回调函数去初始化 async_hooks 接口。

1
2
3
4
5
6
7
8
var asyncHooks = require('async_hooks')
var hooks = {
init: init,
before: before,
after: after,
destroy: destroy
}
var asyncHook = asyncHooks.createHook(hooks)

通过createHook、enable、disable方法可以创建async_hooks、开始追踪、停止追踪:
这里写图片描述
async_hooks 基于一种resource的概念,一个resource会触发上面提到的async_hooks的回调函数。这种resource可以是TTYWRAP 、SSLCONNECTION 或者任何使用 Embedder API 定义的内容(稍后会提到)。如果要使用 http.createServer() 创建一个 server,我们可以看看async_hooks API的init回调函数的resource,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var asyncHooks = require('async_hooks')
var http = require('http')
var fs = require('fs')
var hooks = {
init: init
}
var asyncHook = asyncHooks.createHook(hooks)
asyncHook.enable()
http.createServer(function (req, res) {
res.end('hello qts')
}).listen(8079)
function init (asyncId, type, triggerId) {
fs.writeSync(1, `${type} \n`)
}

2. function init (asyncId, type, triggerId) {}

async_hooks init callback :
async_hooks init callback

init回调函数或许是最有趣的一个回调,它允许我们访问当前resource(current resource),并看看是什么原因触发了它。这就意味着我们最终能创建一个良好的结构来查看我们的应用程序中到底发生了什么。
这里写图片描述

我认为这是一个很棒的trace跟踪方法 : 在init中拿到 asyncId 和 triggerId,启动一个计时器去跟踪一些操作,最后在destroy回调中销毁计时器。按照这个思路,我写了一个 on-async-hook,一个简单的async_hook跟踪事件发射器。

当async_hooks调用init回调函数时,on-async-hook会创建一个trace跟踪,并且根据他们的triggerId来组合 resource。我想给操作计时,因此 on-async-hook 的跟踪结构还包含了开始时间和结束时间,也就是在init、destroy中进行添加和销毁计时器。一个简单的使用 on-async-hook 进行跟踪的结果

3. Embedder API

如果要绑定本地的C++代码,则可能需要自定义resource,这一点可以使用Node 8 提供的实验性 Embedder API 做到。下面是一个例子,使用的是utp-native:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var AsyncResource = require('async_hooks').AsyncResource
var utp = require('utp-native')
var resource = new AsyncResource('UTPNative')
var server = utp.createServer(function (socket) {
socket.pipe(socket)
})
server.listen(1337, function () {
var socket = utp.connect(1337)
resource.emitBefore()
socket.write('hello qts')
resource.emitAfter()
socket.on('data', function (data) {
console.log('resourceId', resource.asyncId())
console.log('triggerAsyncId', resource.triggerAsyncId())
console.log('this is my data ', data)
})
})
server.on('close', function () {
resource.emitDestroy()
})

4. 展望

async_hooks仍然是一个实验性很强的API,它在接下来的时间中可能会不断变化,比如async_hooks.currentId()变成了async_hooks.executionAsyncId() ,但它能为我们提供一种新方法去监视程序,这点就非常炫酷了!



Ω