Надежно подключитесь к MongoDB

ОБНОВЛЕНИЕ: Я использую версию 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

TEST 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, соединение не восстанавливается.

Решения?

Имеет ли эта проблема решение? Если да, знаете ли вы, что это такое? Как только мое приложение начнет запугивать "топология была уничтожена", единственный способ заставить все работать снова - перезапустить все приложение...

Ответы

Ответ 1

Существует два варианта подключения, которые управляют восстановлением драйвера mongo nodejs после сбоя подключения

  • reconnectTries: попытка повторного подключения # раза (по умолчанию 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 секунд, когда перестанете пытаться снова подключиться.

Sidenote: если вы хотите, чтобы приложение/запрос не дожидались истечения периода пересоединения, вам необходимо передать опцию bufferMaxEntries: 0. Цена для этого заключается в том, что запросы также прерываются во время коротких сетевых перерывов.

Ответ 2

По умолчанию монгольский драйвер будет пытаться подключаться 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

package.json: "mongodb": "3.1.3"

Восстановить существующие подключения

Чтобы точно настроить конфигурацию повторного подключения для предварительно установленных подключений, вы можете изменить параметры reconnectTries/reconnectInterval (значения по умолчанию и дополнительную документацию здесь).

Восстановить начальное соединение

Для начального соединения клиент Монго не переподключается, если он обнаружил ошибку (см. ниже). Я полагаю, что так и должно быть, но тем временем я создал следующий обходной путь, используя библиотеку 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

Ответ 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)

Ответ 5

С драйвером 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 как значение по умолчанию.

Ответ 6

Поведение может отличаться в разных версиях драйвера. Вы должны указать версию своего драйвера.

версия драйвера: 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);

Второй аргумент может использоваться для передачи параметров сервера.

Ответ 7

Если бы вы использовали Mongoose для своих схем, стоило бы рассмотреть мой вариант ниже, поскольку mongoose никогда не пытался неявно переподключиться к mongoDB после первой попытки.

Обратите внимание, что я подключаюсь к Azure CosmosDB для API MongoDB. На вашем, может быть, на локальной машине.

Ниже мой код.

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

Вы можете решить экспортировать и повторно использовать этот модуль везде, где вам нужно подключиться к БД через Dependency Injection. Но вместо этого я покажу, как получить доступ к соединению с базой данных.

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

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