You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js Async Hooks中run函数未调用next致Nodemon崩溃

解决Node.js Express中Async Local Storage搭配Nodemon崩溃的问题

问题场景

开发Node.js Express应用时,使用Async Local Storage实现请求ID中间件,相关代码如下:

Async Local Storage文件(asyncContext.ts)

import { AsyncLocalStorage } from 'async_hooks';

const asyncContext = new AsyncLocalStorage<Map<string, string>>();
export default asyncContext;

请求处理中间件

import { Response, NextFunction } from 'express';
import { customAlphabet } from 'nanoid/async';
import { Request } from '../../interfaces/Request';
import asyncLocalStorage from '../asyncContext';

export const requestIdHandler = async (req: Request, res: Response, next: NextFunction) => {
  req.id = await customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 20)();
  const store = new Map<string, string>();
  asyncLocalStorage.run(store, () => {
    store.set('requestId', req.id);
    res.setHeader('X-Request-Id', req.id);
    next();
  });
};

触发任意API时,应用出现Nodemon崩溃错误:

[nodemon] app crashed - waiting for file changes before starting...

移除asyncLocalStorage.run函数后应用恢复正常,且不使用Nodemon直接运行时也能正常工作。

问题原因

Nodemon的热重载机制与Node.js的async_hooks模块(Async Local Storage基于此实现)存在兼容性冲突:

  • async_hooks会追踪所有异步资源的生命周期,Nodemon在应用重启时,无法彻底清理这些异步资源的引用,导致内存泄漏或资源状态异常,最终触发崩溃。
  • 异步中间件内嵌套调用asyncLocalStorage.run,会导致异步上下文的传递逻辑与Nodemon的重启流程产生冲突,加剧了崩溃概率。

解决方案

方法1:改用同步生成请求ID,简化中间件逻辑

nanoid提供同步版本的customAlphabet,将中间件改为同步函数,避免异步上下文与Nodemon的冲突:

import { Response, NextFunction } from 'express';
import { customAlphabet } from 'nanoid'; // 引入同步版本
import { Request } from '../../interfaces/Request';
import asyncLocalStorage from '../asyncContext';

export const requestIdHandler = (req: Request, res: Response, next: NextFunction) => {
  const generateId = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 20);
  req.id = generateId(); // 同步生成请求ID
  const store = new Map<string, string>();
  asyncLocalStorage.run(store, () => {
    store.set('requestId', req.id);
    res.setHeader('X-Request-Id', req.id);
    next();
  });
};

方法2:调整Nodemon配置,优化重启逻辑

在项目根目录创建nodemon.json,添加延迟重启、明确监听范围的配置,确保异步资源有足够时间清理:

{
  "ignore": ["node_modules/", ".git/", "dist/"],
  "watch": ["src/"],
  "execMap": {
    "ts": "ts-node"
  },
  "env": {
    "NODE_ENV": "development"
  },
  "delay": 1000 // 延迟1秒重启,等待异步资源清理完成
}

开发时也可临时禁用async_hooks的追踪(仅用于开发环境,禁止生产使用):

NODE_DISABLE_ASYNC_HOOKS=1 nodemon src/index.ts

方法3:替换为cls-hooked库

如果上述方案无效,可改用兼容性更好的cls-hooked库实现异步上下文管理:

  1. 安装依赖:
npm install cls-hooked
  1. 重构上下文文件(asyncContext.ts):
import { createNamespace } from 'cls-hooked';

const asyncContext = createNamespace('request-context');
export default asyncContext;
  1. 调整中间件:
import { Response, NextFunction } from 'express';
import { customAlphabet } from 'nanoid/async';
import { Request } from '../../interfaces/Request';
import asyncContext from '../asyncContext';

export const requestIdHandler = async (req: Request, res: Response, next: NextFunction) => {
  req.id = await customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 20)();
  asyncContext.run(async () => {
    asyncContext.set('requestId', req.id);
    res.setHeader('X-Request-Id', req.id);
    next();
  });
};

内容的提问来源于stack exchange,提问作者Yash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:47:21