如何让Node服务等待MongoDB启动并实现重连(PM2场景)
解决PM2 Reload时Node服务连接MongoDB失败的问题
嘿,这个场景我太熟悉了!PM2 reload的时候确实会打乱服务启动的顺序,MongoDB需要一点时间完成初始化,但Node服务重启后会立刻尝试连接,结果就扑空了。不过咱们可以从Node端入手,让服务在连接失败时自动重试,结合MongoDB驱动的特性或者自定义逻辑就能搞定,我给你两种实用方案:
方案一:利用MongoDB驱动内置的重连机制
MongoDB官方驱动本身就支持自动重连,只要在创建客户端时配置相关参数,就能让驱动在连接失败时自动重试,不用自己写太多逻辑。示例代码如下:
const { MongoClient } = require('mongodb'); const express = require('express'); // MongoDB连接字符串 const mongoUri = 'mongodb://localhost:27017/your-database-name'; // 配置MongoDB客户端的重连参数 const mongoClient = new MongoClient(mongoUri, { retryWrites: true, reconnectTries: Number.MAX_VALUE, // 设置为无限重试(直到成功) reconnectInterval: 1500, // 每次重试间隔1.5秒 connectTimeoutMS: 30000, // 单次连接超时时间 socketTimeoutMS: 30000 // 套接字超时时间 }); async function initApp() { try { // 尝试连接MongoDB await mongoClient.connect(); console.log('✅ 成功连接到MongoDB'); // 连接成功后再启动Express服务 startExpressServer(mongoClient); } catch (error) { console.error('❌ 初始连接MongoDB失败,驱动将自动重试:', error.message); // 这里可以加个兜底的手动重试,防止驱动的自动重试出意外 setTimeout(initApp, 5000); } } function startExpressServer(client) { const app = express(); // 将MongoDB客户端挂载到app上,方便路由中使用 app.locals.mongoClient = client; // 示例路由 app.get('/', (req, res) => { res.send('服务正常运行,已连接MongoDB'); }); // 启动Express服务 app.listen(3000, () => { console.log('🚀 Express服务已启动在端口3000'); // 如果用PM2的wait_ready配置,记得发送ready信号 process.send?.('ready'); }); } // 初始化应用 initApp();
关键参数说明:
reconnectTries: Number.MAX_VALUE:让驱动无限重试连接,直到成功reconnectInterval: 1500:每次重试的间隔时间,避免过于频繁重试connectTimeoutMS和socketTimeoutMS:设置合理的超时时间,防止单次连接卡住太久
方案二:自定义连接重试逻辑(更灵活可控)
如果你需要更精细的控制(比如限制重试次数、添加告警逻辑),可以自己封装一个带重试的连接函数:
const { MongoClient } = require('mongodb'); const express = require('express'); const mongoUri = 'mongodb://localhost:27017/your-database-name'; const MAX_RETRIES = 12; // 最多重试12次(约24秒) const RETRY_INTERVAL = 2000; // 每次重试间隔2秒 async function connectMongoWithRetry(retryCount = 0) { const client = new MongoClient(mongoUri); try { await client.connect(); return client; } catch (error) { console.error(`⚠️ 连接失败,重试次数: ${retryCount + 1}/${MAX_RETRIES}`, error.message); if (retryCount < MAX_RETRIES) { // 等待指定间隔后重试 await new Promise(resolve => setTimeout(resolve, RETRY_INTERVAL)); return connectMongoWithRetry(retryCount + 1); } else { // 达到最大重试次数,终止服务或触发告警 console.error('❌ 达到最大重试次数,无法连接MongoDB,服务终止'); process.exit(1); } } } async function startApp() { try { const mongoClient = await connectMongoWithRetry(); console.log('✅ MongoDB连接成功'); const app = express(); app.locals.mongoClient = mongoClient; app.get('/', (req, res) => { res.send('服务正常运行,已连接MongoDB'); }); app.listen(3000, () => { console.log('🚀 Express服务已启动在端口3000'); process.send?.('ready'); }); } catch (error) { console.error('❌ 应用启动失败:', error); } } startApp();
这种方式的优势是可以完全掌控重试的节奏和终止条件,比如重试12次后还失败就退出服务,避免无限等待。
额外的PM2配置优化
除了Node端的重试逻辑,还可以优化PM2的配置文件(ecosystem.config.js),让PM2更智能地处理服务启动顺序:
module.exports = { apps: [ { name: 'mongodb', script: 'mongod', args: '--dbpath ./data/db', // 替换成你的MongoDB数据路径 autorestart: true, out_file: './logs/mongodb-out.log', error_file: './logs/mongodb-error.log' }, { name: 'express-server', script: './server.js', // 你的Node服务入口文件 autorestart: true, wait_ready: true, // 等待服务发送ready信号才标记为就绪 listen_timeout: 60000, // 最多等待60秒 out_file: './logs/express-out.log', error_file: './logs/express-error.log', env: { NODE_ENV: 'development' } } ] };
配合前面代码中的process.send('ready'),PM2会等到Express服务真正连接上Mongo并启动后,才会认为服务就绪,避免过早重启或者误判服务状态。
这样一来,不管是pm2 start all还是pm2 reload all,Node服务都会耐心等待MongoDB初始化完成,再也不会因为连接失败而直接挂掉了!
内容的提问来源于stack exchange,提问作者douira
相关产品推荐
相关产品推荐

