我如何连接到代理后面的MongoDB副本集?

我有一个云服务上的MongoDB副本集。 出于安全原因,副本集可用于云的内部networking。

我遵循该云服务指南,并在代理服务器上为副本集的每个成员设置代理:

0.0.0.0:27017 -> member1-private-ip:27107 0.0.0.0:27018 -> member2-private-ip:27107 0.0.0.0:27019 -> member3-private-ip:27017 ... 

我能够以独立模式从公共networking连接到副本集的每个成员:

 mongoUri = new MongoClientURI("mongodb://usr:pwd@proxy-server-public-ip:27017/db") ; client = MongoClient(mongoUri); 

但是当我尝试以副本集模式连接它:

 mongoUri = new MongoClientURI("mongodb://usr:pwd@proxy-server-public-ip:27017,proxy-server-public-ip:27018,proxy-server-public-ip:27019/db?replicaSet=replcaSetName"); client = MongoClient(mongoUri); 

我会失败,连接错误,因为复制集告诉驱动程序使用每个成员的内部地址(公共networking不可访问)。

ps:我可以在代理服务器上以副本集模式连接到副本集。

如何连接到代理服务器后面的副本集?


更新:我在连接时使用代理服务器的公共地址。

五月份我问了这个问题,但现在我来回答我自己的问题。

正如在注释中所讨论的那样,MongoDB服务器将返回其成员列表及其configuration的地址。 由于MongoDB副本集configuration了私有地址,因此MongoDB服务器将提供成员的私有地址。

为了解决这个问题,我们需要一个专门用于Mongo客户端连接的代理。 代理应拦截MongoDB服务器对isMaster命令的响应,并覆盖服务器公共地址或代理服务器地址的私有地址。 客户端收到这些被拦截的地址后,就可以在replicaSet模式下连接这些地址。

以下是Node.js中的一些代码:

 clientConn.on("data", dataHandler(proxyConn, function(data) { const msg = new WireMessage(data); if (msg.isCommand("isMaster")) { remoteClient.recordForInterception(msg); } mongoConn.write(data); })); mongoConn.on("data", dataHandler(proxyConn, function(data) { const msg = new WireMessage(data, {skipBody: true}); var changed = false; if (remoteClient.shouldInterceptReply(msg)) { // To Intercept message, we need a full parse msg.parseBody(); changed = remoteClient.interceptReply(clientConn, msg); } if (changed) { // Serialize intercepted data data = msg.serialize(); } clientConn.write(data); })); interceptReply = function (conn, replyMessage) { if ('isMaster'.toLowerCase() !== replyMessage.toLowerCase()) { return false; } var doc; if (replyMessage.body.metadata) { doc = replyMessage.body.metadata; } else if (replyMessage.body.documents instanceof Array && replyMessage.body.documents.length > 0) { doc = replyMessage.body.documents[0]; } else { this.logger.warn("No document to handle: %s.", replyMessage.toString()); return false; } return interceptHosts(doc, conn, hostMappings); }; interceptHosts = function (doc, conn, mappings) { if (doc.hosts) { var hosts = doc.hosts; for (var i = 0; i < hosts.length; ++i) { var host = hosts[i]; hosts[i] = getReverseAddress(host, conn, mappings); } } if (doc.primary) { doc.primary = getReverseAddress(doc.primary, conn, mappings); } if (doc.me) { doc.me = getReverseAddress(doc.me, conn, mappings); } return doc; }; function getReverseAddress(endpoint, conn, reverseAddressMapping) { var hostMap = reverseAddressMapping[endpoint]; if (hostMap && hostMap.host === "0.0.0.0") { // If we are listening on ANY address, use // the effective address the client connect us. return conn.localAddress + ":" + hostMap.port; } else if (hostMap) { return hostMap.host + ":" + hostMap.port; } else { return endpoint; } } 

更改客户端库以将私有地址映射到公共地址应该是另一种解决scheme。 但支持所有语言并将自定义的库推送到合作伙伴的开发机器可能是一项艰巨的工作。