#mongodb #node.js
#mongodb #node.js
Вопрос:
ОБНОВЛЕНИЕ: я использую версию 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, соединение не восстанавливается.
Решения?
Есть ли у этой проблемы решение? Если да, знаете ли вы, что это такое? Как только мое приложение начнет выдавать сообщение «топология была уничтожена», единственный способ заставить все снова работать — перезапустить все приложение…
Комментарии:
1. Возможно, вы подходите к этой проблеме с неправильного направления. На основе строки подключения ваше приложение подключается к экземпляру базы данных с одним узлом. Если для вашего приложения требуется время безотказной работы, вам следует настоятельно рассмотреть возможность подключения к набору реплик с несколькими узлами, несущими данные. Драйвер, если он настроен правильно, автоматически повторно подключится к новому основному узлу после перехода на другой ресурс.
Ответ №1:
Существует 2 варианта подключения, которые определяют, как драйвер mongo nodejs повторно подключается после сбоя соединения
- Попытки повторного подключения: попытка повторного подключения # раз (по умолчанию 30 раз)
- Повторное подключение: сервер будет ждать # миллисекунд между повторными попытками (по умолчанию 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
. Ценой этого является то, что запросы также прерываются во время коротких перерывов в работе сети.
Комментарии:
1. Я обновил ответ версией драйвера / mongo. Все то же самое? Я спрашиваю, потому что в документе «connect» упоминается
{ server: ... }
: mongodb.github.io/node-mongodb-native/2.1/tutorials/connect Однако документ для «Настроек подключения» (ссылка по этой самой ссылке): mongodb.github.io/node-mongodb-native/2.1/reference/ подключение/… вообще не упоминается{server: ...}
…2. Я тестировал с теми же версиями, и объект options, похоже, работает с ним или без
server:{ ... }
него. Я думаю, это связано с устаревшими причинами. Я удалил это из ответа для простоты. Дайте мне знать, если это сработает для вас.3. О БОЖЕ, я хотел дать награду за этот ответ, и я случайно присвоил его другому!!!
4. Вы дали это мне .. и здесь нет возможности вернуть его.😐
5. @Разработчик уже прошел 30-дневную гарантию возврата ^_ ^
Ответ №2:
package.json: "mongodb": "3.1.3"
Переподключите существующие соединения
Чтобы точно настроить конфигурацию повторного подключения для предварительно установленных подключений, вы можете изменить параметры reconnectTries
/ reconnectInterval
(значения по умолчанию и дополнительную документацию здесь).
Повторное подключение к первоначальному соединению
При первоначальном подключении клиент 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
Комментарии:
1. Привет, Ник, я хотел бы назначить клиенту некоторую глобальную переменную, поэтому могу ли я взять ту
client
, которая возвращается при обратном вызове? например, как изменить всю функцию для поддержки « return MongoClient.connect(url, options, (client) => {globalClient = client}).catch(повторить попытку) ` `2. Привет @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 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);
2-й аргумент может использоваться для передачи параметров сервера.
Комментарии:
1. Я обновил ответ версией драйвера / mongo. Все то же самое?
2. Я спрашиваю, потому что в документе «connect» упоминается
{ server: ... }
: mongodb.github.io/node-mongodb-native/2.1/tutorials/connect Однако документ для «Настроек подключения» (ссылка по этой самой ссылке): mongodb.github.io/node-mongodb-native/2.1/reference/ подключение/… вообще не упоминается{server: ...}
…3. Даже в mongodb.github.io/node-mongodb-native/2.2/reference/connecting/… что относится к 2.2, свойство «сервер» опущено, а параметры передаются прямо из корня объекта (без вложенного
server:
свойства)…
Ответ №7:
Если вы использовали Mongoose для своих схем, было бы целесообразно рассмотреть мой вариант ниже, поскольку mongoose никогда не повторял попыток повторного подключения к MongoDB неявно после неудачной первой попытки.
Пожалуйста, обратите внимание, что я подключаюсь к Azure CosmosDB для 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=trueamp;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.
})();