【问题标题】:Reliably reconnect to MongoDB可靠地重新连接到 MongoDB
【发布时间】:2017-02-08 15:34:51
【问题描述】:

更新:我在驱动程序上使用的是 2.1 版本,而不是 3.2

我有一个使用 MongoDB 的节点应用程序。我遇到的问题是,如果 MongoDB 服务器因任何原因出现故障,应用程序不会重新连接。 为了做到这一点,我的测试基于this official tutorial 中的代码。

var MongoClient = require('mongodb').MongoClient
  , f = require('util').format;

MongoClient.connect('mongodb://localhost:27017/test', 

// Optional: uncomment if necessary
// { db: { bufferMaxEntries: 3 } },


function(err, db) {
  var col = db.collection('t');

  setInterval(function() {
    col.insert({a:1}, function(err, r) {
      console.log("insert")
      console.log(err)

      col.findOne({}, function(err, doc) {
        console.log("findOne")
        console.log(err)
      });
    })
  }, 1000)
});

想法是运行这个脚本,然后停止mongod,然后重新启动它。 所以,我们开始吧:

测试 1:停止 mongod 10 秒

将 MongoDb 停止 10 秒会达到预期的效果:它将停止运行这 10 秒的查询,然后在服务器返回 ip 后运行所有查询

测试 2:停止 mongod 30 秒

正好 30 秒后,我开始得到:

{ [MongoError: topology was destroyed] name: 'MongoError', message: 'topology was destroyed' }
insert

{ [MongoError: topology was destroyed] name: 'MongoError', message: 'topology was destroyed' }

麻烦的是,从这里开始,当我重新启动 mongod 时,连接没有重新建立。

解决方案?

这个问题有解决办法吗?如果是这样,你知道它是什么吗? 一旦我的应用开始吐出“拓扑已被破坏”,让一切恢复正常的唯一方法是重新启动整个应用......

【问题讨论】:

  • 您可能从错误的方向处理此问题。根据连接字符串,您的应用程序正在连接到数据库的单个节点实例。如果您的应用程序需要正常运行时间,您应该强烈考虑连接到具有多个数据承载节点的副本集。如果配置正确,驱动程序将在故障转移后自动重新连接到新的主节点。

标签: mongodb node.js


【解决方案1】:

有 2 个连接选项可控制连接失败后 mongo nodejs 驱动程序如何重新连接

  • reconnectTries:尝试重新连接 #times(默认 30 次)
  • reconnectInterval:服务器将在重试之间等待 # 毫秒 (默认 1000 毫秒)

reference on mongo driver docs

这意味着 mongo 默认会继续尝试连接 30 次,并在每次重试前等待 1 秒。这就是您在 30 秒后开始看到错误的原因。

您应该像此示例一样根据您的需要调整这两个参数。

var MongoClient = require('mongodb').MongoClient,
    f = require('util').format;

MongoClient.connect('mongodb://localhost:27017/test', 
    {
        // retry to connect for 60 times
        reconnectTries: 60,
        // wait 1 second before retrying
        reconnectInterval: 1000
    },

    function(err, db) {
        var col = db.collection('t');

        setInterval(function() {
            col.insert({
                a: 1
            }, function(err, r) {
                console.log("insert")
                console.log(err)

                col.findOne({}, function(err, doc) {
                    console.log("findOne")
                    console.log(err)
                });
            })
        }, 1000)
    });

这将尝试 60 次而不是默认的 30 次,这意味着当它停止尝试重新连接时,您将在 60 秒后开始看到错误。

旁注:如果您想阻止应用程序/请求等到重新连接期到期,您必须传递选项bufferMaxEntries: 0。这样做的代价是请求也会在短暂的网络中断期间中止。

【讨论】:

  • 我用驱动程序/mongo 版本更新了答案。还是一样?我问是因为“连接”文档确实提到了{ server: ... }:mongodb.github.io/node-mongodb-native/2.1/tutorials/connect 但是“连接设置”的文档(从这个链接链接):mongodb.github.io/node-mongodb-native/2.1/reference/connecting/… 根本没有提到{server: ...}...
  • 我用相同的版本进行了测试,选项对象似乎在使用或不使用server:{ ... } 时都可以使用。我想它的存在是出于遗留原因。为简单起见,我将其从答案中删除。让我知道它是否适合您。
  • 哦,天哪,我本来想给这个答案赏金的,结果我不小心把它给了另一个人!!!
  • 你给了我..这里没有退还的选项。?
  • @Developer 已经通过了 30 天的赏金保证 ^_^
【解决方案2】:

package.json: "mongodb": "3.1.3"

重新连接现有连接

要为预先建立的连接微调重新连接配置,您可以修改 reconnectTries/reconnectInterval 选项 (default values and further documentation here)。

重新连接初始连接

对于初始连接,mongo客户端如果遇到错误不会重新连接(见下文)。 I believe it should,但与此同时,我使用 promise-retry 库(使用指数退避策略)创建了 the following workaround。

const promiseRetry = require('promise-retry')
const MongoClient = require('mongodb').MongoClient

const options = {
  useNewUrlParser: true,
  reconnectTries: 60,
  reconnectInterval: 1000,
  poolSize: 10,
  bufferMaxEntries: 0
}

const promiseRetryOptions = {
  retries: options.reconnectTries,
  factor: 1.5,
  minTimeout: options.reconnectInterval,
  maxTimeout: 5000
}

const connect = (url) => {
  return promiseRetry((retry, number) => {
    console.log(`MongoClient connecting to ${url} - retry number: ${number}`)
    return MongoClient.connect(url, options).catch(retry)
  }, promiseRetryOptions)
}

module.exports = { connect }

Mongo 初始连接错误: failed to connect to server [db:27017] on first connect

【讨论】:

  • 嗨尼克,我想将客户端分配给一些全局变量,那么我可以使用回调中返回的client 吗?比如如何修改整个函数以支持``` return MongoClient.connect(url, options, (client) => {globalClient = client}).catch(retry) ```
  • 嗨@user2515512 - 是的,您需要使用Promise.then 或async 函数和await 来获取client... 就像你通常一样,如果您使用的是MongoClient.connect 函数。
【解决方案3】:

默认情况下,Mongo 驱动程序会尝试重新连接 30 次,每秒一次。之后,它将不再尝试重新连接。

您可以将重试次数设置为 Number.MAX_VALUE 以使其“几乎永远”重新连接:

    var connection = "mongodb://127.0.0.1:27017/db";
    MongoClient.connect(connection, {
      server : {
        reconnectTries : Number.MAX_VALUE,
        autoReconnect : true
      }
    }, function (err, db) {

    });

【讨论】:

    【解决方案4】:

    使用 mongodb 驱动程序 3.1.10,您可以将连接设置为

    MongoClient.connect(connectionUrl, {
        reconnectInterval: 10000, // wait for 10 seconds before retry
        reconnectTries: Number.MAX_VALUE, // retry forever
    }, function(err, res) {
        console.log('connected') 
    })
    

    您不必指定 autoReconnect: true,因为这是默认设置。

    【讨论】:

      【解决方案5】:

      发生这种情况是因为它可能已超过重试连接限制。经过多次重试后,它会破坏 TCP 连接并变为空闲状态。所以对于它增加重试次数,如果你增加连接重试之间的差距会更好。

      使用以下选项:

      retryMiliSeconds {Number, default:5000}, number of milliseconds between retries.
      numberOfRetries {Number, default:5}, number of retries off connection.
      

      更多详情请参考此链接https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

      解决方案:

      MongoClient.connect("mongodb://localhost:27017/integration_test_?", {
          db: {
            native_parser: false,
      retryMiliSeconds: 100000,
      numberOfRetries: 100
          },
          server: {
            socketOptions: {
              connectTimeoutMS: 500
            }
          }
        }, callback)
      

      【讨论】:

        【解决方案6】:

        行为可能因不同版本的驱动程序而异。您应该提及您的驱动程序版本。

        驱动程序版本:2.2.10(最新) mongo 数据库版本:3.0.7

        以下代码将延长 mongod 恢复的时间。

        var MongoClient = require('mongodb').MongoClient
          , f = require('util').format;
        
        function connectCallback(err, db) {
          var col = db.collection('t');
        
          setInterval(function() {
            col.insert({a:1}, function(err, r) {
              console.log("insert")
              console.log(err)
        
              col.findOne({}, function(err, doc) {
                console.log("findOne")
                console.log(err)
              });
            })
          }, 1000)
        }
        var options = { server: { reconnectTries: 2000,reconnectInterval: 1000 }} 
        MongoClient.connect('mongodb://localhost:27017/test',options,connectCallback);
        

        第二个参数可用于传递服务器选项。

        【讨论】:

        【解决方案7】:

        如果您将 Mongoose 用于您的 Schemas,那么值得考虑我在下面的选项,因为在第一次尝试失败后,mongoose 永远不会重新尝试重新连接到 mongoDB。

        请注意,我正在连接到 Azure CosmosDB for MongoDB API。在你的可能在本地机器上。

        下面是我的代码。

        const mongoose = require('mongoose');
        
        // set the global useNewUrlParser option to turn on useNewUrlParser for every connection by default.
        mongoose.set('useNewUrlParser', true);
        // In order to use `findOneAndUpdate()` and `findOneAndDelete()`
        mongoose.set('useFindAndModify', false);
        
        async function mongoDbPool() {
        // Closure.
        return function connectWithRetry() {
            // All the variables and functions in here will Persist in Scope.
            const COSMODDBUSER = process.env.COSMODDBUSER;
            const COSMOSDBPASSWORD = process.env.COSMOSDBPASSWORD;
            const COSMOSDBCONNSTR = process.env.COSMOSDBCONNSTR;
        
            var dbAuth = {
                auth: {
                    user: COSMODDBUSER,
                    password: COSMOSDBPASSWORD
                }
            };
            const mongoUrl = COSMOSDBCONNSTR + '?ssl=true&replicaSet=globaldb';
        
            return mongoose.connect(mongoUrl, dbAuth, (err) => {
                if (err) {
                    console.error('Failed to connect to mongo - retrying in 5 sec');
                    console.error(err);
                    setTimeout(connectWithRetry, 5000);
                } else {
                    console.log(`Connected to Azure CosmosDB for MongoDB API.`);
                }
            });
        };}
        

        您可以决定在需要通过依赖注入连接到数据库的任何地方导出和重用此模块。但我现在只展示如何访问数据库连接。

        (async () => {
            var dbPools = await Promise.all([mongoDbPool()]);
            var mongoDbInstance = await dbPools[0]();
        
            // Now use "mongoDbInstance" to do what you need.
        })();
        

        【讨论】:

          猜你喜欢
          • 2011-12-03
          • 2021-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-16
          • 2016-09-02
          相关资源
          最近更新 更多