node 跟踪异步资源(回调)async_hook、AsyncResource

news/2024/11/8 2:40:05/

文章目录

  • 跟踪异步回调的原因
  • 异步调用链基础
  • async_hook
  • AsyncResource
    • asyncResource.runInAsyncScope

跟踪异步回调的原因

  • node 基于事件循环的异步非阻塞 I/O 模型,发起一次异步调用,回调在之后的循环中才被调用,此时已经无法追踪到是谁发起了这个异步调用
    • 比如下面谁的 callback 先调用、以及 callback 属实谁的回调?无法从日志中确认调用链
const fs = require('fs')function callback(err, data) {console.log('callback', data)
}// 无改文件
fs.readFile("a.txt", callback)
console.log('after a')
// 无改文件
fs.readFile("b.txt", callback)
console.log('after b')
// after a
// after b
// callback undefined
// callback undefined

异步调用链基础

  • async scope:每一个函数(不论异步还是同步)都会提供一个上下文, 异步称之为 async scope
  • asyncId:每一个 async scope 中都有一个 asyncId ,它是当前 async scope 的标志,同一个的 async scope 中 asyncId 必然相同,每个异步资源在创建时, asyncId 自动递增,全局唯一
    • 同一个 async scope 可能会被调用及执行多次,不管执行多少次,其 asyncId 必然相同,通过监听函数,我们很方便追踪其执行的次数、时间以及上下文关系
  • triggerAsyncId:每一个 async scope 中都有一个 triggerAsyncId ,用来表示当前函数是由哪个 async scope 触发生成的
  • 通过 asyncId 和 triggerAsyncId 就可以追踪整个异步的调用关系及链路,这个是全链路追踪的核心

async_hook

文档

  • async_hook 提供了一系列钩子用于监听当前执行环境中的所有类型异步回调
  • type:触发异步回调的类型,自定义类型通过 AsyncResource 创建
FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE,HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP,SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP,TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,RANDOMBYTESREQUEST, TLSWRAP, Microtask, Timeout, Immediate, TickObject

示例:

// 监听 fs.readFile 异步回调const fs = require('fs')
const async_hooks = require('async_hooks');
const { fd } = process.stdout;let indent = 0;
async_hooks.createHook({init(asyncId, type, triggerAsyncId) {const eid = async_hooks.executionAsyncId();const indentStr = ' '.repeat(indent);fs.writeSync(fd,`${indentStr}${type}(${asyncId}):` +` trigger: ${triggerAsyncId} execution: ${eid} \n`);},before(asyncId) {const indentStr = ' '.repeat(indent);fs.writeSync(fd, `${indentStr}before:  ${asyncId}\n`);indent += 2;},after(asyncId) {indent -= 2;const indentStr = ' '.repeat(indent);fs.writeSync(fd, `${indentStr}after:  ${asyncId}\n`);},destroy(asyncId) {const indentStr = ' '.repeat(indent);fs.writeSync(fd, `${indentStr}destroy:  ${asyncId}\n`);},
}).enable();function callback(err, data) {console.log('callback', data)
}fs.readFile("a.txt", callback)
console.log('after a')
fs.readFile("b.txt", callback)
console.log('after b')
// 打印结果
FSREQCALLBACK(4): trigger: 1 execution: 1      # a
after a
TickObject(5): trigger: 1 execution: 1
FSREQCALLBACK(6): trigger: 1 execution: 1      # b
after b
before:  5
after:  5
before:  4
callback undefinedTickObject(7): trigger: 4 execution: 4       # trigger by a
after:  4
before:  7
after:  7
before:  6
callback undefinedTickObject(8): trigger: 6 execution: 6       # trigger by b
after:  6
before:  8
after:  8
destroy:  5
destroy:  7
destroy:  4
destroy:  8
destroy:  6

AsyncResource

文档

用于监听自定义异步回调,自定义时一般通过继承 AsyncResource 实现自己的逻辑

  • asyncResource.runInAsyncScope

    • 当配置好 async_hook后 ,new AsyncResource 会触发 init 钩子
    • 通过 runInAsyncScope(fn) 执行异步回调会触发 before-> fn -> after 钩子
    • 通过在 runInAsyncScope 方法中包裹要传入的异步调用。可以保证这个资源( fn )的异步行为是可追踪的

追踪自定义异步回调示例:

const async_hooks = require("async_hooks");const fs = require("fs");
const { fd } = process.stdout;let indent = 0;// 配置好 hook
async_hooks.createHook({init(asyncId, type, triggerAsyncId) {const eid = async_hooks.executionAsyncId();const indentStr = " ".repeat(indent);fs.writeSync(fd,`${indentStr}${type}(${asyncId}):` +` trigger: ${triggerAsyncId} execution: ${eid} \n`);},before(asyncId) {const indentStr = " ".repeat(indent);fs.writeSync(fd, `${indentStr}before:  ${asyncId}\n`);indent += 2;},after(asyncId) {indent -= 2;const indentStr = " ".repeat(indent);fs.writeSync(fd, `${indentStr}after:  ${asyncId}\n`);},destroy(asyncId) {const indentStr = " ".repeat(indent);fs.writeSync(fd, `${indentStr}destroy:  ${asyncId}\n`);},}).enable();function callback(err, data) {console.log("callback", data);
}// 继承 AsyncResource 实现自定义逻辑
class MyResource extends async_hooks.AsyncResource {constructor() {super("my-resource");}close() {this.emitDestroy();}
}function p() {return new Promise((resolve) => {setTimeout(() => {resolve();}, 1000);});
}let resource = new MyResource();
// 追踪自定义异步回调
resource.runInAsyncScope(async () => {console.log("hello");await p();
});resource.close();
// 打印结果
my-resource(4): trigger: 1 execution: 1 
before:  4PROMISE(5): trigger: 4 execution: 4 
helloTickObject(6): trigger: 4 execution: 4 PROMISE(7): trigger: 4 execution: 4 Timeout(8): trigger: 4 execution: 4 PROMISE(9): trigger: 7 execution: 4 
after:  4
before:  6
after:  6
destroy:  4
destroy:  6
before:  8
after:  8
before:  9
after:  9
destroy:  8

http://www.ppmy.cn/news/485303.html

相关文章

vue中 @scroll的使用

@scroll 是 Vue 中的一个修饰符,可以用来监听元素的滚动事件。 使用方法: 在元素上绑定一个 @scroll 修饰符,并绑定一个事件处理函数,如:<template><div class="container" @scroll="handleScroll"></div> </template><sc…

Vue点击上下滑动滚屏

上滑到顶部 绑定点击事件&#xff1a; <div class"totop"><a click"backTop()" size"50" v-show"tabshow" class"btn btn-default"><span class"fa"></span></a></div> 点…

无缝滚动:vue-seamless-scroll

1、安装 npm install vue-seamless-scroll --save2、注册 // 方法一&#xff1a;main.js中全局注册 import vueSeamlessScroll from vue-seamless-scroll Vue.use(vueSeamlessScroll)// 方法二&#xff1a;局部注册 import vueSeamlessScroll from vue-seamless-scroll expor…

vue:使用vuescroll

1、npm下载 npm install vuescroll -S 官网&#xff1a;https://vuescrolljs.yvescoding.org/zh/ <template><div><vuescroll :ops"ops"><div class"container"><ul v-for"(item, index) in 100" :key"index&q…

vue 简单的tab切换滑动效果

实现一个简单的滑动效果&#xff1a; 代码逻辑比较简单&#xff0c;利用css的transform和transition属性实现简单的tab切换效果 贴上代码&#xff0c;仅供参考&#xff1a; html部分&#xff1a; <template><div class"container"><div class"…

【Vue实用功能】Vue中实现移动端的scroll滚动

Vue中实现移动端的scroll滚动 介绍&#xff1a; 在移动端或PC&#xff0c;页面的部分内容需要我们让其在页面滚动&#xff0c;这时候我们都会使用::-webkit-scrollbar来修饰原生滚动条&#xff0c;这样会影响滚动条对宽度的检测&#xff0c;所有就有了该组件&#xff0c;不需要…

Vue【vue-seamless-scroll】滚动组件及注意事项

一、运用场景&#xff1a;VUE开发的项目中需要表格滚动展示信息 二、组件&#xff1a;vue-seamless-scroll 三、使用&#xff1a;通过ul标签css样式模拟表格&#xff0c;表头表数据 安装 npm install vue-seamless-scroll --save在main.js中引入 import scroll from vue-s…

js:Vue.js自定义指令实现scroll下滑滚动翻页

Vue.js自定义指令实现scroll下滑滚动翻页 核心代码 // directives/scroll.jsimport Vue from vue// v-scroll Vue.directive(scroll, {bind(el, binding, vnode) {// console.log(bind)// 此处为了简单&#xff0c;直接判断触底了function handleScroll(e) {let isBottom e.t…