clsService 全局变量(隐形背包)

nestjs-cls 是 NestJS 生态中一个非常流行且强大的库,全称是 NestJS Continuation-Local Storage

用一句话概括:它让你的 NestJS 应用拥有了"请求级别的全局变量"能力。

它的核心作用是解决 "参数透传" (Prop Drilling) 的痛苦,尤其是在处理异步操作(Async)的时候。

为了让你彻底明白,我们对比一下有它没它的区别:

1. 痛点:没有 CLS 时 (参数透传的地狱)

假设你有一个需求:在数据库保存数据时,自动记录是哪个 User ID 创建的。

你的调用链路是:Controller -> Service -> Repository

  • Controller : 从 Request 里拿到 userId,传给 Service。
  • Service : 拿到 userId,做业务逻辑,再传给 Repository。
  • Repository : 拿到 userId,写库。

代码会变成这样:

复制代码
// 每一层都要被迫接收 userId 这个参数,哪怕中间层根本不需要用它,只是为了传给下一层
// Controller
createOrder(user) {
  this.service.create(user.id, data); 
}

// Service
create(userId, data) {
  // Service 层其实不需要 userId,但必须接住它传给 Repo
  this.repo.save(userId, data);
}

// Repository
save(userId, data) {
  db.insert({ ...data, createdBy: userId });
}

问题: 如果调用链有 10 层,你就得改 10 个函数签名,把 userId 一层层传下去。这叫"参数透传",非常难维护。


2. 解决方案:使用 nestjs-cls (隐形背包)

nestjs-cls 基于 Node.js 的 AsyncLocalStorage 技术。它允许你在请求刚进来的时候(比如在 Middleware 或 Guard 里),把 userId 放进一个**"隐形的上下文背包"**里。

在这个请求处理的任何地方、任何层级 (Service, Repository, 甚至工具函数),你都可以直接把手伸进背包里拿到这个 userId,而不需要通过函数参数传递

代码变得很干净:

复制代码
// 1. 在拦截器/中间件里 (请求刚进来)
cls.set('userId', req.user.id); // 放入背包

// ... 中间层函数完全不需要传 userId 参数 ...

// 2. 在深层的 Repository 里 (想用就直接拿)
save(data) {
  // 直接从空气中(上下文)拿到 userId
  const userId = this.cls.get('userId'); 
  db.insert({ ...data, createdBy: userId });
}

3. 它为什么安全?(并发安全)

你可能会问:"这不就是全局变量吗?Node.js 是异步的,多个请求同时进来,会不会张三拿到了李四的 ID?"

不会。 这正是 nestjs-cls (以及底层的 AsyncLocalStorage) 的核心黑科技。

  • 它能保证数据隔离
  • 请求 A 的上下文,只有请求 A 的后续逻辑能访问。
  • 请求 B 进来,会创建一个全新的上下文,互不干扰。

4. 常见的使用场景

除了上面的 UserID 例子,这些场景必用 nestjs-cls

  1. Trace ID / Request ID (全链路追踪)
    • 请求进来生成一个 UUID,存入 CLS。
    • 在代码任何地方打 Log 时,自动从 CLS 取出这个 ID 附带在日志里。这样查日志时,能把属于同一个请求的所有日志串起来。
  1. 多租户系统 (Multi-tenancy)
    • 请求进来判断是哪个公司(Tenant),存入 CLS。
    • 在数据库查询层,自动从 CLS 读取 TenantID,强制加上 WHERE tenant_id = ...,防止数据越权。
  1. 数据库事务 (Transactions)
    • 把数据库的 Transaction Manager 存入 CLS。
    • 这样 Service 层不需要把事务对象传给 Repository,Repository 自动从 CLS 获取当前的事务上下文进行操作。

总结

看到 import { ClsModule } from 'nestjs-cls';,你就知道这个项目使用了上下文管理技术。

它在干的事就是:"帮你在整个请求的处理周期内,偷偷带着一些数据(如用户ID、追踪ID),让你在任何地方都能随时取用,而不用把这些数据写在函数参数里传来传去。"

相关推荐
神奇小汤圆18 小时前
浅析二叉树、B树、B+树和MySQL索引底层原理
后端
文艺理科生18 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling18 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
南极企鹅18 小时前
springBoot项目有几个端口
java·spring boot·后端
Luke君6079718 小时前
Spring Flux方法总结
后端
define952718 小时前
高版本 MySQL 驱动的 DNS 陷阱
后端
忧郁的Mr.Li19 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
暮色妖娆丶20 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
Coder_Boy_20 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring
Java后端的Ai之路20 小时前
【Spring全家桶】-一文弄懂Spring Cloud Gateway
java·后端·spring cloud·gateway