【NestJS】异步本地存储
(Async Local Storage,简称 ALS)是 Node.js提供的一种机制,用于在异步调用链中维护上下文状态,而无需显式地在函数之间传递参数。NestJS 本身没有内置 ALS 抽象,在 NestJs 中实现异步本地存储,可以引用 Node.js 提供 AsyncLocalStorage 类,其本质上是对。在多线程程序中,普通全局变量是共享的,所有线程访问的是同一份内存。是一个专为
一、异步本地存储 ALS
异步本地存储(Async Local Storage,简称 ALS)是 Node.js 提供的一种机制,用于在异步调用链中维护上下文状态,而无需显式地在函数之间传递参数。它的底层基于 async_hooks API,可以类比为“异步版的线程本地存储”。
核心概念
- 上下文传播:在异步操作中自动携带状态(如用户 ID、请求 ID、事务对象等),避免手动传参。
- 调用链隔离:每个异步调用链拥有独立的存储空间,互不干扰。
- 使用方式:通过
AsyncLocalStorage.run(store, callback)创建上下文,后续异步代码可通过getStore()访问。
二、线程本地存储 TLS
上文提到线程本地存储,那什么是线程本地存储?
线程本地存储(Thread Local Storage,TLS)是一种让每个线程拥有自己独立变量副本的机制,避免了多线程访问共享变量时的竞争问题。它的实现方式因语言和平台而异,但核心思想是:为每个线程维护一份私有的数据副本。
核心原理
在多线程程序中,普通全局变量是共享的,所有线程访问的是同一份内存。而 TLS 则通过以下方式实现线程隔离:
- 每个线程启动时,系统为其分配一块专属的 TLS 区域。
- TLS 变量在编译时或运行时被映射到该区域的某个偏移位置。
- 线程访问 TLS 变量时,实际上是访问自己 TLS 区域中的副本。
三、NodeJs中的应用
在 Node.js 中,ALS(AsyncLocalStorage) 是 async_hooks 模块提供的一个高级封装,用于在异步调用链中存储和访问上下文数据。它非常适合实现像请求级别的 traceId、用户信息等“线程局部变量”的功能。
import { AsyncLocalStorage } from 'async_hooks';
const asyncLocalStorage = new AsyncLocalStorage<{ traceId: string }>();
function logWithContext(message: string) {
const store = asyncLocalStorage.getStore();
console.log(`[${store?.traceId || 'no-trace'}] ${message}`);
}
function handleRequest(traceId: string) {
asyncLocalStorage.run({ traceId }, () => {
logWithContext('Start processing');
setTimeout(() => {
logWithContext('Finished processing');
}, 100);
});
}
handleRequest('req-123');
handleRequest('req-456');
输出示例:
[req-123] Start processing
[req-456] Start processing
[req-123] Finished processing
[req-456] Finished processing
注意事项
AsyncLocalStorage只在异步链中有效,不能跨线程或跨进程。- 在某些异步边界(如
process.nextTick或第三方库)可能会丢失上下文,需谨慎测试。 - 不建议用于存储大量数据,仅用于轻量级上下文信息。
四、NestJs中的实现
NestJS 本身没有内置 ALS 抽象,在 NestJs 中实现异步本地存储,可以引用 Node.js 提供 AsyncLocalStorage 类,其本质上是对 async_hooks 的封装,自动追踪异步资源之间的上下文关系。
1 创建 ALS 实例并注入为服务
// als.module.ts
import { Module } from '@nestjs/common';
import { AsyncLocalStorage } from 'async_hooks';
@Module({
providers: [
{
provide: AsyncLocalStorage,
useValue: new AsyncLocalStorage(),
},
],
exports: [AsyncLocalStorage],
})
export class AlsModule {}
2 使用中间件初始化上下文
// app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AsyncLocalStorage } from 'async_hooks';
@Module({
imports: [AlsModule],
})
export class AppModule implements NestModule {
constructor(private readonly als: AsyncLocalStorage<any>) {}
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => {
const store = { userId: req.headers['x-user-id'] };
this.als.run(store, () => next());
})
.forRoutes('*');
}
}
3 在服务中访问上下文数据
@Injectable()
export class CatsService {
constructor(private readonly als: AsyncLocalStorage<any>) {}
getCatForUser() {
const store = this.als.getStore();
const userId = store?.userId;
return `获取用户 ${userId} 的猫咪数据`;
}
}
五、NestJs中的应用
nestjs-cls 是一个专为 NestJS 设计的封装库,它基于 Node.js 的 AsyncLocalStorage,但提供了更优雅、更强大的开发体验。相比直接使用原生 ALS,它在类型安全、模块集成、传输方式兼容性等方面都有显著优势。
nestjs-cls 的优势
| 功能点 | 原生 AsyncLocalStorage | nestjs-cls |
|---|---|---|
| NestJS DI 集成 | ❌ 手动注入 | ✅ 自动注入 ClsService |
| 类型安全 | ❌ 无泛型支持 | ✅ 支持泛型定义上下文结构 |
| 多传输支持 | ❌ 仅支持 HTTP | ✅ 支持 GraphQL、WebSocket、Cron 等 |
| 中间件封装 | ❌ 需手动编写 | ✅ 自动挂载 ClsMiddleware |
| 插件机制 | ❌ 无 | ✅ 支持事务插件、请求 ID 插件等 |
| 测试友好 | ❌ 难以 mock | ✅ 可模拟上下文数据 |
如何使用 nestjs-cls
1 安装依赖
npm install nestjs-cls
2 注册模块
// app.module.ts
import { Module } from '@nestjs/common';
import { ClsModule } from 'nestjs-cls';
@Module({
imports: [
ClsModule.forRoot({
middleware: { mount: true }, // 自动挂载中间件
}),
],
})
export class AppModule {}
3 设置上下文数据(如用户 ID)
// auth.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private readonly cls: ClsService) {}
use(req: any, res: any, next: () => void) {
this.cls.set('userId', req.headers['x-user-id']);
next();
}
}
4 在服务中访问上下文
// cats.service.ts
import { Injectable } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
@Injectable()
export class CatsService {
constructor(private readonly cls: ClsService) {}
getCatForUser() {
const userId = this.cls.get('userId');
return `获取用户 ${userId} 的猫咪数据`;
}
}
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)