Node绑定全局TraceID的实现方法

2020-06-17 05:35:09易采站长站整理

console.log(`top level: ${executionAsyncId()} ${triggerAsyncId()}`);

const f = () => {
console.log(`f: ${executionAsyncId()} ${triggerAsyncId()}`);
};

f();

const g = () => {
console.log(`setTimeout: ${executionAsyncId()} ${triggerAsyncId()}`);
setTimeout(() => {
console.log(`inner setTimeout: ${executionAsyncId()} ${triggerAsyncId()}`);
}, 0);
};

setTimeout(g, 0);
setTimeout(g, 0);

在上述代码中,我们使用 setTimeout 模拟一个异步调用过程,且在该异步过程中我们调用了 handler 同步函数,我们在每个函数内都输出其对应的 Async ID 和 Trigger Async ID 。执行上述代码后,其运行结果如下。


top level: 1 0
f: 1 0
setTimeout: 7 1
setTimeout: 9 1
inner setTimeout: 11 7
inner setTimeout: 13 9

通过上述日志输出,我们得出以下信息:

调用同步函数,不会改变其 Async ID ,如函数 f 内的 Async ID 和其调用者的 Async ID 相同。
同一个函数,被不同时刻进行异步调用,会分配至不同的 Async ID ,如上述代码中的 g 函数。

追踪异步资源

正如我们前面所说的,Async Hooks 可用于追踪异步资源。为了实现此目的,我们需要了解 Async Hooks 的相关 API ,具体说明参照以下代码中的注释。


const asyncHooks = require("async_hooks");

// 创建一个 AsyncHooks 实例。
const hooks = asyncHooks.createHook({
// 对象构造时会触发 init 事件。
init: function(asyncId, type, triggerId, resource) {},
// 在执行回调前会触发 before 事件。
before: function(asyncId) {},
// 在执行回调后会触发 after 事件。
after: function(asyncId) {},
// 在销毁对象后会触发 destroy 事件。
destroy: function(asyncId) {}
});

// 允许该实例中对异步函数启用 hooks 。
hooks.enable();

// 关闭对异步资源的追踪。
hooks.disable();

我们在调用 createHook 时,可注入 init 、 before 、 after 和 destroy 函数,用于 追踪异步资源的不同生命周期 。

全新解决方案

基于 Async Hooks API ,我们即可设计以下解决方案,实现日志与请求记录的绑定,即 Trace ID 的全局绑定。


const asyncHooks = require("async_hooks");
const { executionAsyncId } = asyncHooks;

// 保存异步调用的上下文。
const contexts = {};

const hooks = asyncHooks.createHook({
// 对象构造时会触发 init 事件。
init: function(asyncId, type, triggerId, resource) {
// triggerId 即为当前函数的调用者的 asyncId 。
if (contexts[triggerId]) {
// 设置当前函数的异步上下文与调用者的异步上下文一致。