配置网络访问后本地服务器仍无法稳定连接MongoDB
本地服务器连接MongoDB Atlas后频繁断开的排查与解决
我开发的项目需要同时从Heroku服务器和本地开发服务器连接MongoDB实例,Heroku端连接交互正常,但本地服务器连接建立后很快断开,无手动终止或未处理异常导致进程崩溃的情况。已将MongoDB Atlas网络访问配置为0.0.0.0/0允许任意IP访问,问题仍存在。
相关代码
db.js
const mongoose = require("mongoose"); mongoose.set("strictQuery", true); const isDev = process.env.NODE_ENV !== "production"; const dbURI = process.env.MONGODB_URI; mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true, }); mongoose.connection.on("connected", () => { console.log("MongoDB connection established!"); }); mongoose.connection.on("error", (error) => { console.error(`MongoDB connection error: ${error}`); process.exit(-1); }); //New Unhandled exceptions method mongoose.connection.on("disconnected", (error) => { if (error) { console.error(`MongoDB connection disconnected: ${error}`); process.exit(-1); } else { console.log(error); process.exit(0); } }); // Handle SIGINT event for graceful shutdown process.on("SIGINT", (err) => { if (err) { console.error(err); } connection.close(() => { console.log("MongoDB connection closed due to app termination"); process.exit(0); }); });
server.js
// Load the required packages const express = require("express"); const nodemon = require("nodemon"); const dotenv = require("dotenv"); const cors = require("cors"); // Load the environment variables from the .env file dotenv.config(); // Initial database connection const db = require("./data/db"); // Import routes const linkButtonRoute = require("./routes/linkButtonRoute"); // Create an express app const app = express(); app.set("view engine", "ejs"); // Use the express.json() middleware to parse incoming request bodies app.use(express.json()); app.use(cors()); // Set the port for the application const port = process.env.PORT || 5000; // Mount the routes to the express app app.use("/api/linkButton", linkButtonRoute); // Start the server app.listen(port, () => { console.log(`Server listening on port ${port}`); });
报错日志
C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\connection.js:825 const serverSelectionError = new ServerSelectionError(); ^ MongooseServerSelectionError: connect ETIMEDOUT 15.185.70.41:27017 at Connection.openUri (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\connection.js:825:32) at C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\index.js:419:10 at C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\helpers\promiseOrCallback.js:41:5 at new Promise (<anonymous>) at promiseOrCallback (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\helpers\promiseOrCallback.js:40:10) at Mongoose._promiseOrCallback (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\index.js:1272:10) at Mongoose.connect (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongoose\lib\index.js:418:20) at Object.<anonymous> (C:\Users\Lior\AppBar-Server\node-js-getting-started\data\db.js:8:10) at Module._compile (node:internal/modules/cjs/loader:1159:14) at Module._extensions..js (node:internal/modules/cjs/loader:1213:10) { reason: TopologyDescription { type: 'ReplicaSetNoPrimary', servers: Map(3) { 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017' => ServerDescription { address: 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017', type: 'Unknown', hosts: [], passives: [], arbiters: [], tags: {}, minWireVersion: 0, maxWireVersion: 0, roundTripTime: -1, lastUpdateTime: 183346427, lastWriteDate: 0, error: MongoNetworkError: connect ETIMEDOUT 15.185.70.41:27017 at connectionFailureError (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongodb\lib\cmap\connect.js:387:20) at TLSSocket.<anonymous> (C:\Users\Lior\AppBar-Server\node-js-getting-started\node_modules\mongodb\lib\cmap\connect.js:310:22) at Object.onceWrapper (node:events:628:26) at TLSSocket.emit (node:events:513:28) at emitErrorNT (node:internal/streams/destroy:151:8) at emitErrorCloseNT (node:internal/streams/destroy:116:3) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { cause: Error: connect ETIMEDOUT 15.185.70.41:27017 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16) { errno: -4039, code: 'ETIMEDOUT', syscall: 'connect', address: '15.185.70.41', port: 27017 }, [Symbol(errorLabels)]: Set(1) { 'ResetPool' } }, topologyVersion: null, setName: null, setVersion: null, electionId: null, logicalSessionTimeoutMinutes: null, primary: null, me: null, '$clusterTime': null }, 'ac-0qzaeb7-shard-00-00.nxgkbuq.mongodb.net:27017' => ServerDescription { address: 'ac-0qzaeb7-shard-00-00.nxgkbuq.mongodb.net:27017', type: 'RSSecondary', hosts: [ 'ac-0qzaeb7-shard-00-00.nxgkbuq.mongodb.net:27017', 'ac-0qzaeb7-shard-00-01.nxgkbuq.mongodb.net:27017', 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017' ], passives: [], arbiters: [], tags: { region: 'ME_SOUTH_1', nodeType: 'ELECTABLE', provider: 'AWS', workloadType: 'OPERATIONAL' }, minWireVersion: 0, maxWireVersion: 17, roundTripTime: 321.9200000000001, lastUpdateTime: 183346522, lastWriteDate: 2023-05-23T13:11:54.000Z, error: null, topologyVersion: { processId: ObjectId { [Symbol(id)]: [Buffer [Uint8Array]] }, counter: 11 }, setName: 'atlas-yyc43k-shard-0', setVersion: 2, electionId: null, logicalSessionTimeoutMinutes: 30, primary: 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017', me: 'ac-0qzaeb7-shard-00-00.nxgkbuq.mongodb.net:27017', '$clusterTime': { clusterTime: Timestamp { low: 2, high: 1684847514, unsigned: true }, signature: { hash: [Binary], keyId: [Long] } } }, 'ac-0qzaeb7-shard-00-01.nxgkbuq.mongodb.net:27017' => ServerDescription { address: 'ac-0qzaeb7-shard-00-01.nxgkbuq.mongodb.net:27017', type: 'RSSecondary', hosts: [ 'ac-0qzaeb7-shard-00-00.nxgkbuq.mongodb.net:27017', 'ac-0qzaeb7-shard-00-01.nxgkbuq.mongodb.net:27017', 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017' ], passives: [], arbiters: [], tags: { provider: 'AWS', workloadType: 'OPERATIONAL', region: 'ME_SOUTH_1', nodeType: 'ELECTABLE' }, minWireVersion: 0, maxWireVersion: 17, roundTripTime: 322.56000000000006, lastUpdateTime: 183346518, lastWriteDate: 2023-05-23T13:11:54.000Z, error: null, topologyVersion: { processId: ObjectId { [Symbol(id)]: [Buffer [Uint8Array]] }, counter: 11 }, setName: 'atlas-yyc43k-shard-0', setVersion: 2, electionId: null, logicalSessionTimeoutMinutes: 30, primary: 'ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net:27017', me: 'ac-0qzaeb7-shard-00-01.nxgkbuq.mongodb.net:27017', '$clusterTime': { clusterTime: Timestamp { low: 2, high: 1684847514, unsigned: true }, signature: { hash: [Binary], keyId: [Long] } } } }, stale: false, compatible: true, heartbeatFrequencyMS: 10000, localThresholdMS: 15, setName: 'atlas-yyc43k-shard-0', maxElectionId: null, maxSetVersion: null, commonWireVersion: 0, logicalSessionTimeoutMinutes: 30 }, code: undefined } Node.js v18.12.0
解决思路
1. 排查网络层面问题
- 本地网络可能存在防火墙或代理限制,导致无法持续连接MongoDB Atlas的主节点(报错显示主节点
ac-0qzaeb7-shard-00-02.nxgkbuq.mongodb.net连接超时)。尝试关闭本地防火墙或切换网络(比如用手机热点)测试。 - 检查本地是否能ping通
15.185.70.41,或者用telnet测试27017端口是否能连通:telnet 15.185.70.41 27017,如果不通,说明网络层面存在阻断。
2. 修正代码中的错误
db.js里的SIGINT处理函数中,connection变量未定义,应该改为mongoose.connection,否则会导致未捕获的引用错误,可能间接影响连接稳定性:process.on("SIGINT", (err) => { if (err) { console.error(err); } mongoose.connection.close(() => { // 修正为mongoose.connection console.log("MongoDB connection closed due to app termination"); process.exit(0); }); });disconnected事件的处理逻辑有问题:该事件触发时不一定会传入error参数,当前代码在无错误时会打印undefined并直接退出进程,这会导致正常的连接断开(比如心跳检测暂时失败)直接终止程序。建议调整逻辑,不要在无错误时直接退出,而是尝试重连:mongoose.connection.on("disconnected", () => { console.log("MongoDB connection disconnected, attempting to reconnect..."); mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true, }); });
3. 优化Mongoose连接配置
- 针对副本集连接,添加
retryWrites: true和w: 'majority'配置,提升连接稳定性:mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true, retryWrites: true, w: 'majority' }); - 可以增加连接超时和心跳间隔的配置,适配本地网络情况:
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true, serverSelectionTimeoutMS: 5000, // 服务器选择超时时间 heartbeatFrequencyMS: 30000 // 心跳间隔,减少频繁检测 });
4. 检查MongoDB Atlas配置
- 确认Atlas的副本集状态正常,主节点是否在线。可以登录Atlas控制台查看集群状态,确认主节点没有故障。
- 重新生成MongoDB连接字符串,确保没有包含错误的参数,比如是否正确选择了
Node.js驱动版本,连接字符串中的用户名、密码是否正确。
内容的提问来源于stack exchange,提问作者liorlsa9
相关产品推荐
相关产品推荐

