如何在Express中间件中判断MongoDB集群是否存在主节点且可写入?
Great question! Let's tackle both parts of your problem step by step—first checking for a primary node in the cluster, then detecting read-only mode when connected with connectWithNoPrimary: true in your Express middleware.
1. 客户端判断MongoDB集群是否存在主节点
不管你用原生MongoDB驱动还是Mongoose,都可以通过isMaster管理命令获取集群状态详情。这个命令会返回当前连接节点及整个集群的元数据信息。
在Mongoose中,你可以从已建立的连接中获取底层的管理数据库,然后执行这个命令:
async function clusterHasPrimary() { try { const adminDb = mongoose.connection.db.admin(); const clusterStatus = await adminDb.command({ isMaster: 1 }); // 两个判断逻辑: // - `ismaster`:当前连接的节点是主节点时为true // - `primary`:集群存在主节点时会返回该字段(即使我们连接的是从节点) return clusterStatus.ismaster || !!clusterStatus.primary; } catch (err) { console.error('检查主节点状态出错:', err); return false; } }
这个函数会返回true,只要集群存在主节点(不管客户端直接连接的是主节点还是从节点)。
2. 在Express中间件中判断是否处于只读状态(无主节点时)
你说得对,mongoose.connection.readyState只能告诉你连接是否已建立,无法直接判断是否允许写操作。当使用connectWithNoPrimary: true时,即使集群没有主节点,连接状态也会显示为就绪(readyState = 1),但所有写操作都会失败。
要在中间件中提前检测这种只读状态,我们可以复用isMaster命令来判断集群是否支持写操作:
async function checkWritableConnection(req, res, next) { try { const adminDb = mongoose.connection.db.admin(); const clusterStatus = await adminDb.command({ isMaster: 1 }); // 集群可写的条件: // - 当前直接连接到主节点(`ismaster: true`) // 或者 // - 集群存在主节点,且当前连接的不是只读从节点 const isWritable = clusterStatus.ismaster || (!!clusterStatus.primary && !clusterStatus.secondary); if (!isWritable) { return res.status(503).json({ error: '集群处于只读模式,无法执行写操作。' }); } next(); } catch (err) { console.error('检查可写状态出错:', err); return res.status(500).json({ error: '集群状态验证失败' }); } } // 在需要写权限的路由上使用这个中间件 app.post('/api/records', checkWritableConnection, async (req, res) => { // 这里写你的写操作逻辑(比如创建文档) });
优化提示
如果担心每次写请求都执行isMaster命令带来性能开销,可以缓存结果(比如将可写状态存在变量中,每隔5-10秒用setInterval刷新一次),这样能平衡状态新鲜度和性能。
另外需要注意:当连接到没有主节点的集群(且connectWithNoPrimary: true)时,isMaster的返回结果会是ismaster: false、没有primary字段,且secondary: true(如果连接的是从节点)——这些都是写操作被禁用的标志。
内容的提问来源于stack exchange,提问作者janober

