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),让你在任何地方都能随时取用,而不用把这些数据写在函数参数里传来传去。"

相关推荐
她说..1 小时前
策略模式+工厂模式实现审批流(面试问答版)
java·后端·spring·面试·springboot·策略模式·javaee
梦梦代码精2 小时前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
李慕婉学姐3 小时前
【开题答辩过程】以《基于Spring Boot的疗养院理疗管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
tb_first3 小时前
SSM速通2
java·javascript·后端
一路向北⁢3 小时前
Spring Boot 3 整合 SSE (Server-Sent Events) 企业级最佳实践(一)
java·spring boot·后端·sse·通信
风象南3 小时前
JFR:Spring Boot 应用的性能诊断利器
java·spring boot·后端
爱吃山竹的大肚肚3 小时前
微服务间通过Feign传输文件,处理MultipartFile类型
java·spring boot·后端·spring cloud·微服务
毕设源码-邱学长5 小时前
【开题答辩全过程】以 基于Springboot的酒店住宿信息管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
咖啡啡不加糖6 小时前
Grafana 监控服务指标使用指南:打造可视化监控体系
java·后端·grafana
gAlAxy...6 小时前
SpringBoot Servlet 容器全解析:嵌入式配置与外置容器部署
spring boot·后端·servlet