node.js 可靠地重新连接到 MongoDB

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39785036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:35:59  来源:igfitidea点击:

Reliably reconnect to MongoDB

mongodbnode.js

提问by Merc

UPDATE:I am using the 2.1 version on the driver, against 3.2

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

I have a node application that uses MongoDB. The problem I have is that if the MongoDB server goes down for any reason, the application doesn't reconnect. To get this right, I based my tests on the code in this official tutorial.

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

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)
});

The idea is to run this script, and then stop mongod, and then restart it. So, here we go:

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

TEST 1: stopping mongod for 10 seconds

测试 1:停止 mongod 10 秒

Stopping MongoDb for 10 seconds does the desired result: it will stop running the queries for those 10 seconds, and then will run all of them once the server is back ip

停止 MongoDb 10 秒会得到预期的结果:它将停止运行这 10 秒的查询,然后在服务器恢复 ip 后运行所有查询

TEST 2: stopping mongod for 30 seconds

测试 2:停止 mongod 30 秒

After exactly 30 seconds, I start getting:

整整 30 秒后,我开始得到:

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

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

The trouble is that from this on, when I restart mongod, the connection is not re-establised.

麻烦的是,从此以后,当我重新启动 mongod 时,无法重新建立连接。

Solutions?

解决方案?

Does this problem have a solution? If so, do you know what it is? Once my app starts puking "topology was destroyed", the only way to get everything to work again is by restarting the whole app...

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

回答by gafi

There are 2 connection options that control how mongo nodejs driver reconnects after connection fails

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

  • reconnectTries: attempt to reconnect #times (default 30 times)
  • reconnectInterval: Server will wait # milliseconds between retries (default 1000 ms)
  • reconnectTries:尝试重新连接#times(默认30次)
  • reconnectInterval:服务器将在重试之间等待 # 毫秒(默认 1000 毫秒)

reference on mongo driver docs

mongo 驱动程序文档参考

Which means that mongo will keep trying to connect 30 times by default and wait 1 second before every retry. Which is why you start seeing errors after 30 seconds.

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

You should tweak these 2 parameters based on you needs like this sample.

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

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)
    });

This will try 60 times instead of the default 30, which means that you'll start seeing errors after 60 seconds when it stops trying to reconnect.

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

Sidenote: if you want to prevent the app/request from waiting until the expiration of the reconnection period you have to pass the option bufferMaxEntries: 0. The price for this is that requests are also aborted during short network interruptions.

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

回答by Nick Grealy

package.json: "mongodb": "3.1.3"

包.json: "mongodb": "3.1.3"

Reconnect existing connections

重新连接现有连接

To fine-tune the reconnect configuration for pre-established connections, you can modify the reconnectTries/reconnectIntervaloptions (default values and further documentation here).

要为预先建立的连接微调重新连接配置,您可以修改reconnectTries/reconnectInterval选项(此处为默认值和更多文档)。

Reconnect initial connection

重新连接初始连接

For the initial connection, the mongo client does not reconnect if it encounters an error (see below). I believe it should, but in the meantime, I've created the following workaroundusing the promise-retrylibrary (which uses an exponential backoff strategy).

对于初始连接,mongo 客户端在遇到错误时不会重新连接(见下文)。我相信它应该,但与此同时,我已经使用库(使用指数退避策略)创建了以下解决方法promise-retry

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 Initial Connect Error:failed to connect to server [db:27017] on first connect

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

回答by Javier Ferrero

By default the Mongo driver will try to reconnect 30 times, one every second. After that it will not try to reconnect again.

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

You can set the number of retries to Number.MAX_VALUE to keep it reconnecting "almost forever":

您可以将重试次数设置为 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) {

    });

回答by Sachin

It's happening because it might have crossed the retry connection limit. After number of retries it destroy the TCP connection and become idle. So for it increase the number of retries and it would be better if you increase the gap between connection retry.

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

Use below options:

使用以下选项:

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

For more details refer to this link https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

有关更多详细信息,请参阅此链接https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

Solution:

解决方案:

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

回答by Rico Chen

With mongodb driver 3.1.10, you can set up your connection as

使用 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') 
})

You do not have to specify autoReconnect: trueas that's the default.

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

回答by inaitgaJ

Behavior may differ with different versions of driver. You should mention your driver version.

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

driver version : 2.2.10 (latest) mongo db version : 3.0.7

驱动程序版本:2.2.10(最新)mongo db 版本:3.0.7

Below code will extend the time mongod can take to come back up.

下面的代码将延长 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);

2nd argument can be used to pass server options.

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

回答by STREET MONEY

If you was using Mongoose for your Schemas, it would be worth considering my option below since mongoose was never retrying to reconnect to mongoDB implicitly after first attempt failed.

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

Kindly note I am connecting to Azure CosmosDB for MongoDB API. On yours maybe on the local machine.

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

Below is my code.

下面是我的代码。

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.`);
        }
    });
};}

You may decide to export and reuse this module everywhere you need to connect to db via Dependency Injection. But instead I will only show how to access the database connection for now.

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

(async () => {
    var dbPools = await Promise.all([mongoDbPool()]);
    var mongoDbInstance = await dbPools[0]();

    // Now use "mongoDbInstance" to do what you need.
})();