有趣的 Async hooks 模块

2023-11-13

在 Node.js 中,Async hooks 是一个非常有意思且强大的模块(虽然性能上存在一些问题),在 APM 中,我们可以借助这个模块做很多事情。本文介绍两个有趣的用法。

AsyncLocalStorage

在 Node.js 中,上下文传递一直是一个非常困难的问题,Node.js 通过 AsyncLocalStorage 提供了一种解决方案,今天看到一个库中实现了类似 AsyncLocalStorage 的能力,还挺有意思的。代码如下。

class ALS {
    constructor() {
        this._contexts = new Map();
        this._stack = [];
        this.hook = createHook({
            init: this._init.bind(this),
            before: this._before.bind(this),
            after: this._after.bind(this),
            destroy: this._destroy.bind(this),
        });
    }

    context() {
        return this._stack[this._stack.length - 1];
    }

    run(context, fn, thisArg, ...args) {
        this._enterContext(context);
        try {
            return fn.call(thisArg, ...args);
        }
        finally {
            this._exitContext();
        }
    }

    enable() {
        this.hook.enable();
    }

    disable() {
        this.hook.disable();
        this._contexts.clear();
        this._stack = [];
    }

    _init(asyncId) {
        const context = this._stack[this._stack.length - 1];
        if (context !== undefined) {
            this._contexts.set(asyncId, context);
        }
    }
    
    _destroy(asyncId) {
        this._contexts.delete(asyncId);
    }
    
    _before(asyncId) {
        const context = this._contexts.get(asyncId);
        this._enterContext(context);
    }
    
    _after() {
        this._exitContext();
    }
    
    _enterContext(context) {
        this._stack.push(context);
    }
    
    _exitContext() {
        this._stack.pop();
    }
}

这个方式是基于 Async hooks 实现的,原理是在 init 钩子中获取当前的上下文,然后把当前的上下文传递到当前创建的异步资源的,接着在执行异步资源回调前,Node.js 会执行 before 钩子,before 钩子中会把当前异步资源(正在执行回调的这个资源)的上下文压入栈中,然后在回调里就可以通过 context 函数获取到当前的上下文,实际上获取的就是刚才压入栈中的内容,执行完回调后再出栈。前面介绍了其工作原理,主要是实现异步资源的上下文传递且在执行回调时通过栈的方式实现了上下文的管理,那么第一个上下文是如何来的呢?答案是通过 run 函数(Node.js 中还可以通过 enterWith),run 会把用户设置的上下文压入栈中,然后执行了一个用户传入的函数,如果这个函数中创建了异步资源,那么用户传入的上下文就会传递到这个新创建的异步资源中,后面执行这个异步资源的回调时,就可以拿到对应的上下文了。接着看一下使用效果。


const als = new ALS();
als.enable();

http.createServer((req, res) => {
    als.run(req, () => {
        setImmediate(() => {
            console.log(als.context().url);
            res.end();
        });
    })
}).listen(9999, () => {
    http.get({ port: 9999, host: '127.0.0.1' });
});

执行上面代码会输出 /。可以看到在 setImmediate 的回调中(setImmediate 会创建一个异步资源)成功拿到了 run 时设置的上下文。

监控异步回调的耗时

在 Node.js 中,代码执行耗时是一个非常值得关注的地方,Node.js 也提供了很多手段采集代码执行的耗时信息,下面介绍的是基于 Async hooks 实现的回调函数耗时监控。

const { createHook } = require('async_hooks');
const fs = require('fs');

const map = {};

createHook({
    init: (asyncId) => {
        map[asyncId] = { stack: new Error().stack };
    },
    before: (asyncId) => {
        if (map[asyncId]) {
            map[asyncId].start = Date.now();
        }
    },
    after: (asyncId) => {
        if (map[asyncId]) {
            fs.writeFileSync(1, `callback cost: ${Date.now() - map[asyncId].start}, stack: ${map[asyncId].stack}`);
        }
    },
    destroy: (asyncId) => {
        delete map[asyncId];
    }
}).enable();

setTimeout(() => {
    for (let i = 0; i < 1000000000; i++) {

    }
});

实现原理非常简单,主要是利用 before 和 after 钩子实现了回调的耗时统计,就不多介绍,社区中也有同学实现了这个能力,具体可以参考 https://github.com/naugtur/blocked-at/tree/master。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有趣的 Async hooks 模块 的相关文章

随机推荐

  • 【爬虫】一、BeautifulSoup库

    文档内容为本人观看北京理工大学嵩天老师公开课的听课笔记与实践总结 图片为从该课程下载资料的截图 感谢嵩老师 Key point 网页内容提取实际上是对标签的内容进行提取 其关键是标签的获取和标签感兴趣内容的提取 获取标签用beautiful
  • win10计算机设备感叹号,win10网络适配器出现感叹号的解决方法

    Win10系统仍然在不断完善 所以用户在使用过程中总会遇到一些陌生的问题 比如 有位用户在新装或重装的Win10系统中 就碰到了网卡不能安装 或安装出错 安装好网卡不能加载等等各种网卡驱动问题 今天小编就为大家简单的介绍一下Win10系统安
  • vtk光照、颜色、相机、坐标系统及空间变换

    1 vtkLight常的方法有 SetColor 设置光照的颜色 以RGB的形式指定颜色 SetPosition 设置光照位置 SetFocalPoint 设置光照焦点 SetIntensity 设置光照的强度 SetSwitch Swit
  • jsrender的基本使用

    1 什么是jsrender 一个JavaScript库 允许您定义一次样板结构并重复使用它来动态生成HTML JsRender为HTML5开发带来了一个新的模板库 它具有无代码标记语法和高性能 不依赖于jQuery 也不依赖于文档对象模型
  • Go-新手速成-流程语句

    1if Go的if不建议写 over if条件判断 age 16 if age lt 18 fmt Println 未成年 2for循环 Go摈弃了while和do while 循环 因为他做到了极简 也不要括号 这么写可以 total 0
  • Pandas知识点-reset_index,reindex,reindex_like,你分得清吗?

    Pandas知识点 reset index reindex reindex like 你分得清吗 reset index 用法详解 reset index 是pandas中将索引重置成自然数的方法 不会改变原始数据的内容和排列顺序 Data
  • 2023年第五届清洁能源与智能电网国际会议(CCESG 2023)

    2023年第五届清洁能源与智能电网国际会议 CCESG 2023 重要信息 会议网址 www ccesg org 会议时间 2023年11月3 5日 召开地点 广西 南宁 截稿时间 2023年10月3日 录用通知 投稿后2周内 收录检索 E
  • Python3基础入门

    文章目录 前言 基础说明 Python安装 Windows Ubuntu 开发环境 程序编写 模块和包 模块 module 包 package pip和换源 总结 前言 Python是目前非常流行的编程语言 这篇文章将对其相关入门内容进行说
  • JS判断数据类型的5种方法

    我们先来了解一下JS中数据类型有哪些 基本数据类型 值类型 String Number boolean null undefined symbol es6新增的 引用数据类型 引用类型 object 包含 Function Array Da
  • CSS line-height概念与举例

    本文同时发表在https github com zhangyachen zhangyachen github io issues 37 定义 两行文字基线之间的距离 基线的大体位置 基线的位置可以看成x字母下边缘的位置 不同字体的基线位置会
  • 微信公众号H5音频视频自动播放(安卓,苹果)

    我们都知道音频视频的自动播放被浏览器或者微信给限制了 必须用户跟页面交互才可以播放音视频 解决办法就是引入微信的jssdk 然后监听 WeixinJSBridgeReady 来实现自动播放 引入jssdk 音频或视频自动播放 documen
  • 查看运行的java程序的几种方式

    windows 任务管理器可以查看进程和线程数 也可以用来杀死进程 tasklist 查看进程 tasklist 杀死进程 linux ps ef 查看所有进程 ps ft p 查看某个进程 PID 的所有线程 kill 杀死进程 top
  • 【转载】手把手教你用 “三步法” 快速实现 4K+ 超高分辨率满细节出图

    手把手教你用 三步法 快速实现 4K 超高分辨率满细节出图 https ngabbs com read php tid 35888357 rand 488 准备工作 如果你的显存不足以直出你期望的最终分辨率 请先按照你习惯的方式安装 切片扩
  • uniapp - Map地图组件属性示例

    目录 1 markers 点标记 用于在地图上显示标记的位置 2 点聚合 3 polygons 4 include points 可以实现自动缩放展示视图内所有的点标记 5 polyline 线 map uni app官网 1 marker
  • littleVGL学习笔记5——lv_obj 基础对象

    1 介绍 littleVGL 是以对象为概念的 而其最核心的基础对象是 lv obj 控件 其他的所有专用控件 比如按钮 标签 列表等 都是在此 lv obj 对象的基础上衍生出来的 所有的控件对象都具有一些共同的属性 如下所示 位置 Po
  • JUC 十二. ReentrantReadWriteLock 与 StampedLock

    目录 一 基础 二 ReentrantReadWriteLock 的锁降级 三 StampedLock 邮戳票据锁 一 基础 ReentrantReadWriteLock 可以看为读读共享 读写 写写依然互斥 总结一句话 读写互斥 读读共享
  • 数字化时代-26:不要做数字空间的难民

    网络是人们新的生存空间 年轻人出生后就存在的空间 与人类社会原先的现实空间并存的人与人交流的空间 在这个空间中 没有自己位置的人 将成为未来社会的难民 年轻人 特别是毕业后的年轻人 需要思考 个人在数字空间中的落脚点和位置 数字原住民 在数
  • Ubuntu 20.04-NVIDIA显卡驱动-安装和卸载-解决黑屏问题

    这一步很重要 202300704更新 黑屏问题主要由linux内核更新导致 一定要保持当前的内核 也就是安装 NVIDIA 驱动时用的内核 sudo apt mark hold linux image generic linux heade
  • Cuda矩阵运算库cuBLAS介绍

    文章目录 简介 cuBLAS库新特性 cuBLAS代码热身 cublasSetMatrix cudaMalloc cublasSscal 源代码 cuBLAS 辅助函数 上下文管理 复制矩阵 数据类型标示 cuBLAS 运算函数 矩阵相乘
  • 有趣的 Async hooks 模块

    在 Node js 中 Async hooks 是一个非常有意思且强大的模块 虽然性能上存在一些问题 在 APM 中 我们可以借助这个模块做很多事情 本文介绍两个有趣的用法 AsyncLocalStorage 在 Node js 中 上下文