Cluster 模式下 mongoose.connect() 不能写在主进程里,因为每个 worker 进程是独立 V8 实例,不共享连接;必须在每个 worker 启动时单独调用 connect 并配置合理的 maxPoolSize(如 4 个 worker、总连接上限 200,则设为 50),且 Model 首次注册须在连接成功后,退出时需显式调用 mongoose.disconnect() 确保优雅关闭。

Cluster 模式下 mongoose.connect() 为什么不能写在主进程里
主进程调用 mongoose.connect() 后,子进程(worker)并不会继承这个连接实例——Node.js 的 cluster 是进程隔离的,每个 worker 都是全新 V8 实例,全局变量、数据库连接、socket 句柄全不共享。你看到“连上了”,其实是每个 worker 自己又连了一次,形成 N 倍连接数。
常见错误现象:MongoServerSelectionError: connect ECONNREFUSED 或 MongoDB Atlas 报警“连接数超限”,但代码里只调了一次 connect。
- 必须把连接逻辑放到 worker 进程启动时执行,而不是
cluster.fork()前 - 主进程只负责派生,绝不碰数据库驱动
- 如果用了
require('mongoose')但没调connect,那只是加载了模块,不算建立连接
每个 worker 独立连接池,但 maxPoolSize 得按总并发算
默认 maxPoolSize: 100,4 个 worker 就可能占用 400 个 socket。MongoDB 服务端有连接数上限(尤其免费版 Atlas 只有 500),容易被踢掉旧连接,导致 worker 出现 Topology was destroyed。
实际配置要倒推:比如你集群开 4 个 worker,MongoDB 允许 200 连接,那每个 worker 的连接池就得设成 maxPoolSize: 50,还得加 minPoolSize: 5 防冷启抖动。
-
mongoose.connect(uri, { maxPoolSize: 50, minPoolSize: 5 })必须显式传参,别依赖默认值 - 连接字符串里带
&maxPoolSize=100会被 driver 忽略,driver 只认 JS 对象参数 - 连接池大小和 worker 数量强相关,上线前务必压测验证
Model 定义可以全局,但 mongoose.connection 是 per-worker 的
很多人把 Schema 和 Model 定义放在 models/user.js 里并导出,然后在入口文件 require('./models') ——这没问题。但若在定义 Model 前就访问 mongoose.connection.readyState,或者试图复用主进程的 connection,就会出错。
典型错误:在主进程里 const User = mongoose.model('User', schema),然后 worker 里直接用 User.find() ——这时 Model 内部仍绑定着主进程不存在的 connection,抛 Connection is disconnected。
- Model 定义可以提前 require,但首次
mongoose.model()调用必须发生在 worker 连接成功后 - 稳妥做法:在 worker 的
mongoose.connect().then(() => { require('./models') })里加载 Model - 不要跨进程传递
mongoose.connection或 Model 实例,它们不是可序列化的
重启/热更时连接泄漏:process.on('SIGTERM') 不等于连接自动关
Cluster 模式下,worker 收到 SIGTERM 后若只调 process.exit(0),mongoose 默认不会等待连接池优雅关闭,残留连接可能卡在 CLOSE_WAIT 状态,下次启动时报 EADDRINUSE 或连接数持续上涨。
必须手动触发 mongoose.disconnect() 并等它 resolve,再退出。注意:这不是同步操作,也不能靠 process.exit() 强退。
- worker 里加:
process.on('SIGTERM', () => mongoose.disconnect().then(() => process.exit(0))) - 别用
mongoose.connection.close(),它不清理内部计时器,disconnect 才完整 - 如果你用 PM2,还要配
kill-timeout: 5000,给 disconnect 留够时间
最常被忽略的是连接池大小和 worker 数量的联动关系——调小 maxPoolSize 却忘了改 worker 数,或反过来,结果要么扛不住流量,要么连爆 MongoDB。真上线前,用 mongostat 或 Atlas 监控页盯着 “Connections” 曲线看三分钟,比读十遍文档都管用。










