可靠地重新连接到 MongoDB

Reliably reconnect to MongoDB

提问人:Merc 提问时间:9/30/2016 最后编辑:Merc 更新时间:9/24/2019 访问量:43353

问:

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

我有一个使用 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)
});

这个想法是运行这个脚本,然后停止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 节点 .js

评论

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

答:

38赞 gafi 10/3/2016 #1

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

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

MONGO 驱动程序文档参考

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

您应该根据需要调整这 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)
    });

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

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

评论

0赞 Merc 10/3/2016
我用 driver/mongo 版本更新了答案。还是一样的吗?我之所以问,是因为“连接”文档确实提到了:mongodb.github.io/node-mongodb-native/2.1/tutorials/connect 但是“连接设置”的文档(从这个链接链接):mongodb.github.io/node-mongodb-native/2.1/reference/connecting/...... 根本没有提到......{ server: ... }{server: ...}
0赞 gafi 10/3/2016
我使用相同的版本进行了测试,并且选项对象似乎可以使用或不使用.我想它的存在是出于遗留原因。为了简单起见,我将其从答案中删除。让我知道它是否适合您。server:{ ... }
0赞 Merc 10/10/2016
哦,我的天,我本来想把赏金给这个答案的,我不小心把它给了另一个人!!
0赞 Sachin 10/11/2016
你给了我..这里没有退货的选项。 😐
0赞 Hendy Irawan 9/28/2021
@Developer已经通过了 30 天的赏金保证 ^_^
1赞 inaitgaJ 10/3/2016 #2

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

驱动程序版本 : 2.2.10 (最新) MONGO DB版本: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);

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

评论

0赞 Merc 10/3/2016
我用 driver/mongo 版本更新了答案。还是一样的吗?
0赞 Merc 10/3/2016
我之所以问,是因为“连接”文档确实提到了:mongodb.github.io/node-mongodb-native/2.1/tutorials/connect 但是“连接设置”的文档(从这个链接链接):mongodb.github.io/node-mongodb-native/2.1/reference/connecting/...... 根本没有提到......{ server: ... }{server: ...}
0赞 Merc 10/3/2016
即使在 2.2 的 mongodb.github.io/node-mongodb-native/2.2/reference/connecting/... 中,也省略了“server”属性,参数直接从对象的根目录传递(没有嵌套属性)......server:
5赞 Javier Ferrero 10/3/2016 #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) {

    });
3赞 Sachin 10/6/2016 #4

发生这种情况是因为它可能已超过重试连接限制。重试次数后,它会破坏 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)
8赞 Nick Grealy 8/24/2018 #5

package.json:"mongodb": "3.1.3"

重新连接现有连接

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

重新连接初始连接

对于初始连接,如果遇到错误,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 初始连接错误: failed to connect to server [db:27017] on first connect

评论

0赞 jezrael 7/25/2019
嗨,尼克,我想将客户端分配给某个全局变量,那么我可以接受回调中返回的变量吗?比如如何修改整个函数以支持 ''' return MongoClient.connect(url, options, (client) => {globalClient = client}).catch(retry) '''client
1赞 Nick Grealy 7/25/2019
嗨@user2515512 - 是的,您要么需要使用或带有 an 的函数来获得......就像您平时一样,如果您正在使用该功能。Promise.thenasyncawaitclientMongoClient.connect
4赞 Rico Chen 11/30/2018 #6

使用 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

0赞 STREET MONEY 9/24/2019 #7

如果您将 Mongoose 用于您的模式,那么值得考虑我在下面的选择,因为 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.
})();