错误的 Singleton 实现导致我们在 NestJS 中的 Redis 连接崩溃

发布: (2026年2月3日 GMT+8 01:05)
4 min read
原文: Dev.to

Source: Dev.to

单例模式

如果需要回顾一下:单例模式 确保一个类在整个应用生命周期中只有唯一的实例。

  • 该实例在应用启动时创建一次。
  • 所有其他服务共享这唯一实例,而不是每次都创建新实例,这对数据库或消息中间件等资源密集型任务至关重要。

NestJS 中的依赖注入 (DI)

单例行为与依赖注入紧密耦合。在 NestJS 中,Singleton 是 DI 的默认作用域

  • 在启动阶段,NestJS 会在 IoC(控制反转)容器 中注册这些依赖。
  • 要使用服务,只需在类构造函数中注入,NestJS 会处理其余工作。

“遗留”错误:DI 出错时

在 NestJS 中,@Injectable() 装饰器默认使用单例作用域。如果需要让服务在整个应用中可用(如 Config、Logger 或共享客户端),可以使用 @Global() 装饰器。

我们遇到的问题

尽管服务已经标记为单例,遗留代码仍在多个模块的 providers 数组中手动重新声明该服务,并且不必要地过度使用 @Inject() 注解。

结果: 开发者形成了“复制‑粘贴”模式。每当一个新服务需要与另一个微服务通信时,都会意外触发 新实例ClientProxy。这导致 Redis 活跃连接数量激增,形成巨大的 瓶颈,并拖慢整个基础设施。

解决方案

思路很直接,但需要彻底清理:

  1. 集中模块 – 创建专门的 ClientProxyModule 来存放 ClientProxyService
  2. 全局装饰器 – 为该模块添加 @Global() 装饰器,使其在根层级只初始化一次。
  3. 重构 – 删除其他服务中多余的 provider 声明和不必要的 @Inject() 注解,恢复使用标准的构造函数注入。

调试小技巧

如何判断单例是否处理正确?这里有两个快速技巧:

  • 构造函数日志 – 在服务的 constructor 中加入 console.log。重启应用后,如果日志出现 多次,说明出现了实例泄漏。它应该只在启动时打印一次。
  • Getter 日志 – 如果使用 getter 方法,在其中加入日志,以监控服务被访问的具体时间和位置。

结语

无论你使用 NestJS、Spring Boot 还是 .NET,单例模式 的核心原则始终重要。

  • 理解作用域 – 不要盲目复制‑粘贴。了解框架如何管理实例。
  • 审计连接 – 无论是单体还是微服务,“盲目注入”都可能导致资源耗尽。

检查一下你当前的项目——是否在不经意间滥用注入?让我们保持连接整洁,系统高速。

祝编码愉快! 🚀

Back to Blog

相关文章

阅读更多 »

为个人理财启动 NestJS API

概述:大多数在线的 NestJS 教程仅停留在基本的 CRUD 操作或简单的 TODO 列表应用。在本系列中,我们将超越这些初学者的内容。