【问题标题】:Trouble getting Mongoose to reconnect to nodes and send requests to secondaries无法让 Mongoose 重新连接到节点并向辅助节点发送请求
【发布时间】:2015-11-19 21:55:19
【问题描述】:

mongoose 连接到副本集的常用连接字符串如下所示

var connection = mongoose.createConnection("mongodb://db_1:27017/client_test,mongodb://db_2:27017/client_test", { 
    replSet : { rs_name : "rs0", poolSize : 5, socketOptions : { keepAlive : 1 } }
}, function(err) {
    if (err) { throw err; }
});

这样的问题是,如果两台主机中的一台出现故障,那么它将无法连接。如果您只指定一台主机,则最终不会向辅助主机发送任何请求。

这是我对该声明的证明。如果您指定一台主机,并设置您的副本集,以便有一个主服务器和一个仲裁器,然后执行查询,例如

myApi.find({}).slaveOk().read("s").exec(function(err, docs) { 
    console.log(docs) 
})

它会返回结果。好吧,因为我指定了“s”(辅助),所以这个查询应该抛出一个错误,因为没有正在运行的辅助。此外,如果您将辅助服务器联机,然后执行db.currentOp(true),您将永远不会看到任何实际查询以它的方式发送。

当您更改连接字符串以指定每个主机时,您将看到连接转到辅助服务器。现在的困境是,因为你必须在连接字符串中指定额外的主机,如果辅助节点离线,它将无法连接,我们现在已经失去了故障转移(或整个指向副本集)

我无法确定这是我的配置错误、Mongoose 中的错误,还是我对副本集功能方式的理解存在概念缺陷。从一些文档中,他们似乎说从辅助文件中读取基本上是一个坏主意,但这样做的原因通常是陈旧数据的问题。我的问题与过期日期没有任何关系,我想不出一种方法来设置系统,以便在不丢失故障转移能力的情况下获取对辅助节点的查询。

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    1.connection 字符串只是定义了种子服务器,mongodb驱动尝试连接到这些服务器并获取replicaSet中其他服务器的信息(通过调用rs.status())。你可以有 5 个节点的副本集,但在连接字符串中只指定一个,但如果连接字符串中的服务器可用,驱动程序将能够找到其他四个。

    2.我的建议是使用secondaryPreferred 而不是只使用secondary,这样如果没有可用的辅助服务器,就会向主服务器发出请求。

    【讨论】:

    • 这是有道理的,除非我指定一个种子服务器,请求最终不会转到辅助服务器(无论我使用sp 还是s)。使用secondary 是为了证明这一点,因为没有辅助启动它应该会失败。
    【解决方案2】:

    好的,我相信我已经解决了所有问题。这是我学到的。

    1. 在连接字符串中指定所有可能的副本节点,否则 Mongoose 将永远不会在那里发送请求。 Mongoose 对此有一个特定的格式,它不同于 node-mongodb-native 驱动程序。下面的例子。

    2. 1234563初始连接。如果节点稍后上线,它仍然可用。
    3. 您的 mongodb 副本设置中的主机名条目需要与您的应用程序的连接字符串中的主机名条目匹配,并且所有主机名都需要可以从各方访问(mongo 到 mongo 和应用到mongo)。在我的例子中,我将 mongo 到 mongo 的主机名别名为 mongo1:27017mongo2:27017mongo3:27017。我的应用程序服务器使用了带有 IP 的连接字符串。 Mongoose 试图使用 mongo1:27017 主机名(我的应用程序服务器无法访问)而不是我在连接字符串中指定的 IP 地址重新启动连接。这导致它永远不会重新连接到它失去联系的节点。如果我使用了应用程序可以访问的主机名,它仍然可以工作,但我认为最好的做法是使连接字符串和副本设置相同,以删除可能出现错误的地方。

    4. 在您rs.initiate()可能需要将主机名更新为所有框(其他 mongodb 和应用程序服务器可以访问)的值。默认情况下,它可能会以localhost 之类的主机名结束,这意味着每台机器上的名称都不同。这可以来自像这样的盒子mongo shell。

    例子:

    // from mongo shell
    conf = rs.conf()
    conf.members[0].host = "mongo1:27017"
    rs.reconfig(conf)
    

    在节点之间成功故障转移的最终正常运行的连接字符串,包括在查询的目标是辅助节点但没有辅助节点时抛出错误。

    var connection = mongoose.createConnection("mongodb://mongo1:27017/client_test,mongo2:27017/client_test,mongo3:27017/client_test", { 
        replset : { rs_name : "rs0", poolSize : 5, socketOptions : { keepAlive : 1, connectTimeoutMS : 1000 } },
    }, function(err) {
        if (err) { throw err; }
    });
    

    工作副本设置

    {
            "_id" : "rs0",
            "version" : 4,
            "members" : [
                    {
                            "_id" : 0,
                            "host" : "mongo1:27017"
                    },
                    {
                            "_id" : 1,
                            "host" : "mongo2:27017"
                    },
                    {
                            "_id" : 2,
                            "host" : "mongo3:27017",
                            "arbiterOnly" : true
                    }
            ]
    }
    

    【讨论】:

      【解决方案3】:

      我在处理副本时遇到了一些与您类似的问题,在我的情况下,我有 1 个优先级为 10 的主节点、1 个次要优先级为 0(用于分析)和一个仲裁器。 重新连接主实例后,我的写入会失败,我经历了很多尝试来修复它,这是我学到的最重要的事情:

      当我的主要成员关闭或无法访问时,必须有另一个成员有资格成为主要成员。(我的集合中至少有 2 个成员的优先级必须 >= 1)。 如果我只有仲裁者、隐藏者或优先级为 0 的成员, 即使在我重新连接主服务器后,查询也会卡住,我的客户是 无法完成写查询。读取查询仍然有效,但 写不会。

      这就是我面对 mongoose 时遇到的问题,即使设置了 keepalive、autoreconnect 以及所有套接字和连接超时 MS。

      希望这会有所帮助。

      【讨论】:

      • @user3658423 我在上面发布了我的答案,您是否面临类似的问题?您可以发布您的问题的链接,我会检查出来。
      猜你喜欢
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      • 2014-06-25
      • 1970-01-01
      • 2013-09-13
      • 2013-04-16
      相关资源
      最近更新 更多