NestJS 7.x中如何通过请求作用域提供者动态创建并在请求完成后关闭MongoDB连接?
NestJS 7 + Mongoose 5 请求级动态连接 自动释放实现
现有代码核心问题
- 存在多处运行时错误:
- 方法内引用错误:
createMongooseOptions中直接引用未注入到类实例的request对象,运行时会抛出ReferenceError,正确引用应该是this.request.params.uri - 导入路径拼写错误:模块文件导入时将
mongoose错写为mognoose,会导致模块加载失败
- 方法内引用错误:
- 依赖的模块托管逻辑无法实现连接自动释放:
@nestjs/mongoose 7.0.0默认会将生成的Mongoose连接注册为全局单例,存储在全局连接注册表中,哪怕将配置类设置为Scope.REQUEST,NestJS 也只会在请求结束时销毁配置类实例,不会主动销毁已经创建的连接实例,最终造成连接泄漏,天生无法满足请求结束关闭连接的核心诉求。
正确实现方案
绕过@nestjs/mongoose 自带的全局连接托管机制,手动在请求生命周期内完成连接创建、业务使用、连接释放的全流程管控,适配mongoose 5.10.1 的API特性:
- 实现请求作用域的连接管理服务,绑定请求生命周期的销毁钩子
- 基于请求携带的数据库地址,创建独立的Mongoose连接实例
- 在请求被销毁的生命周期钩子中,强制关闭连接释放资源
核心代码实现
请求级连接管理服务
import { Inject, Injectable, OnDestroy, Scope } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; import * as mongoose from 'mongoose'; @Injectable({ scope: Scope.REQUEST }) export class DatabaseConnectionService implements OnDestroy { private connection: mongoose.Connection; constructor( @Inject(REQUEST) private readonly request: Request ) {} // 供业务层获取当前请求绑定的独立连接 getConnection(): mongoose.Connection { if (!this.connection) { // 从请求参数中读取目标数据库连接地址 const targetDbUri = this.request.params.uri; // 创建独立连接,不绑定全局mongoose实例,避免连接污染 this.connection = mongoose.createConnection(targetDbUri, { // 适配mongoose 5.10.1 的兼容配置 useNewUrlParser: true, useUnifiedTopology: true, // 每个请求的连接池大小按需调整,避免占用过多Mongo资源 poolSize: 5, }); } return this.connection; } // 请求结束时,NestJS 销毁当前作用域实例时自动触发该钩子 onDestroy() { if (this.connection) { // 传入true强制中断所有挂起查询,完全关闭连接释放资源 this.connection.close(true); } } }
模块注册
import { Module } from '@nestjs/common'; import { DatabaseConnectionService } from './database-connection.service'; @Module({ providers: [DatabaseConnectionService], exports: [DatabaseConnectionService], }) export class DatabaseModule {}
业务层使用方式
所有依赖请求级连接的业务服务,必须和连接管理服务保持相同的Scope.REQUEST 作用域,避免跨作用域依赖报错:
import { Injectable, Scope } from '@nestjs/common'; import * as mongoose from 'mongoose'; import { DatabaseConnectionService } from './database-connection.service'; @Injectable({ scope: Scope.REQUEST }) export class UserService { private userModel: mongoose.Model<any>; constructor( private readonly dbConnection: DatabaseConnectionService ) { const connection = this.dbConnection.getConnection(); // 通过独立连接实例注册对应业务Schema this.userModel = connection.model('User', new mongoose.Schema({ name: String, email: String, })); } async getUserList() { return this.userModel.find().exec(); } }
关键注意事项
- 必须使用
mongoose.createConnection()创建独立连接,禁止使用mongoose.connect(),后者会修改全局mongoose 的默认连接状态,造成跨请求的连接污染 - 所有依赖请求级数据库连接的服务,必须保持和
DatabaseConnectionService一致的请求作用域配置,否则会出现依赖注入报错、连接实例为undefined的问题 - 连接关闭时必须传入
true参数,强制中断未完成的异步查询,避免连接因为挂起操作无法正常释放 - 高并发场景下可对相同目标URI做短时间(10~30s)的连接缓存,避免同一数据库地址短时间内反复创建/关闭连接造成的性能损耗
内容的提问来源于stack exchange,提问作者Gowtham Raj
相关产品推荐
相关产品推荐

