可靠地重新连接到MongoDB

时间:2016-09-29 16:46:44

标签: mongodb node.js

更新:我在驱动程序上使用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时,连接不会重新建立

解决方案?

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

7 个答案:

答案 0 :(得分:22)

有两个连接选项可以控制连接失败后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。这样做的代价是在短暂的网络中断期间请求也会中止。

答案 1 :(得分:5)

默认情况下,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) {

    });

答案 2 :(得分:3)

package.json:"mongodb": "3.1.3"

重新连接现有连接

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

重新连接初始连接

对于初始连接,mongo客户端在遇到错误时不会重新连接(请参阅下文)。 I believe it should,但与此同时,我已经使用the following workaround库(使用指数补偿策略)创建了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

答案 3 :(得分:2)

它正在发生,因为它可能超过了重试连接限制。重试次数后,它会破坏TCP连接并变为空闲状态。因此,为了增加重试次数,如果增加连接重试之间的差距会更好。

使用以下选项:

""

有关详细信息,请参阅此链接enter image description here

<强>解决方案:

String content = "ALPHA_/image/journal/article?img_id=24810&amp;t=1475128689597_BRAVO";
String regex = "\\/image\\/journal\\/article\\?img_id=\\d+&amp;t=\\d+";
Pattern pattern = Pattern.compile(regex);

Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
    String replacement = matcher.replaceAll("PK");
    System.out.println(replacement); // Will print ALPHA_PK_BRAVO
}

答案 4 :(得分:1)

不同版本的驱动程序的行为可能会有所不同。你应该提一下你的驱动版本。

驱动程序版本: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);

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

答案 5 :(得分:1)

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

std::for_each(arguments.begin(), arguments.end(),
    [&list](std::tuple<std::string, std::type_index, Value> &arg)
{
    if (std::get<2>(arg))
        list.push_back(std::make_pair(std::get<0>(arg), std::get<2>(arg)));
}

您不必指定MongoClient.connect(connectionUrl, { reconnectInterval: 10000, // wait for 10 seconds before retry reconnectTries: Number.MAX_VALUE, // retry forever }, function(err, res) { console.log('connected') }) ,因为这是默认设置。

答案 6 :(得分:0)

如果您在模式上使用Mongoose,则值得在下面考虑我的选择,因为在第一次尝试失败后,mongoose不会再尝试隐式重新连接到mongoDB。

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

下面是我的代码。

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

您可能决定在需要通过依赖注入连接到db的任何地方导出并重用此模块。但是相反,我现在只会显示如何访问数据库连接。

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

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