Каков правильный способ взаимодействия с монгодскими соединениями?
Я пытаюсь выполнить node.js с помощью mongodb (2.2.2), используя собственный накопитель node.js на 10gen.
Сначала все шло хорошо. Но, придя к эталонной части concurrency, произошло много ошибок. Частое соединение/закрытие с 1000 совпадений может привести к тому, что mongodb отклонит любые дополнительные запросы с ошибкой, например:
Error: failed to connect to [localhost:27017]
Error: Could not locate any valid servers in initial seed list
Error: no primary server found in set
Кроме того, если многие клиенты закрываются без явного закрытия, для обнаружения и закрытия их потребуется mongodb минут. Это также вызовет аналогичные проблемы с подключением. (Используя/var/log/mongodb/mongodb.log, чтобы проверить состояние соединения)
Я много пробовал. Согласно руководству, mongodb не имеет ограничений на соединение, но параметр poolSize, по-видимому, не имеет для меня последствий.
Поскольку я работал с ним только в node -mongodb-native модуле, я не очень уверен, что в конечном итоге вызвало проблему. Как насчет производительности на других других языках и драйверах?
PS: В настоящее время использование автономного пула - единственное решение, которое я выяснил, но его использование не может решить проблему с набором реплик. Согласно моему тесту, набор реплик кажется гораздо менее подключенным, чем автономный mongodb. Но понятия не имею, почему это происходит.
Concurrency тестовый код:
var MongoClient = require('mongodb').MongoClient;
var uri = "mongodb://192.168.0.123:27017,192.168.0.124:27017/test";
for (var i = 0; i < 1000; i++) {
MongoClient.connect(uri, {
server: {
socketOptions: {
connectTimeoutMS: 3000
}
},
}, function (err, db) {
if (err) {
console.log('error: ', err);
} else {
var col = db.collection('test');
col.insert({abc:1}, function (err, result) {
if (err) {
console.log('insert error: ', err);
} else {
console.log('success: ', result);
}
db.close()
})
}
})
}
Решение с общим пулом:
var MongoClient = require('mongodb').MongoClient;
var poolModule = require('generic-pool');
var uri = "mongodb://localhost/test";
var read_pool = poolModule.Pool({
name : 'redis_offer_payment_reader',
create : function(callback) {
MongoClient.connect(uri, {}, function (err, db) {
if (err) {
callback(err);
} else {
callback(null, db);
}
});
},
destroy : function(client) { client.close(); },
max : 400,
// optional. if you set this, make sure to drain() (see step 3)
min : 200,
// specifies how long a resource can stay idle in pool before being removed
idleTimeoutMillis : 30000,
// if true, logs via console.log - can also be a function
log : false
});
var size = [];
for (var i = 0; i < 100000; i++) {
size.push(i);
}
size.forEach(function () {
read_pool.acquire(function (err, db) {
if (err) {
console.log('error: ', err);
} else {
var col = db.collection('test');
col.insert({abc:1}, function (err, result) {
if (err) {
console.log('insert error: ', err);
} else {
//console.log('success: ', result);
}
read_pool.release(db);
})
}
})
})
Ответы
Ответ 1
Так как Node.js является однопоточным, вы не должны открывать и закрывать соединение для каждого запроса (например, вы делали бы это в других многопоточных средах).
Это цитата от человека, который написал клиентский модуль MongoDB Node.js:
"Вы открываете приложение MongoClient.connect один раз, когда ваше приложение загружается и повторно используется объект db. Это не единичный пул соединений. создает новый пул соединений. Поэтому откройте его, как только [d] повторное использование во всех запросы". - christkv https://groups.google.com/forum/#!msg/node-mongodb-native/mSGnnuG8C1o/Hiaqvdu1bWoJ
Ответ 2
Изучив рекомендации Гектора. Я нахожу, что соединение Mongodb сильно отличается от некоторых других баз данных, которые я когда-либо использовал. Основное отличие - это собственный диск в nodejs: у MongoClient есть собственный пул соединений для каждого открытого MongoClient, размер пула которого определяется
server:{poolSize: n}
Итак, откройте 5 соединений MongoClient с poolSize: 100, означает общее 5 * 100 = 500 соединений с целевым Mongodb Uri. В этом случае частые открытые и закрытые соединения MongoClient, безусловно, будут огромным бременем для хоста и, наконец, вызовут проблемы с подключением. Вот почему у меня так много проблем.
Но поскольку мой код был написан таким образом, поэтому я использую пул соединений для хранения единственного соединения с каждым отдельным URI и использую простой параллельный ограничитель такого же размера, что и poolSize, чтобы избежать ошибок загрузки.
Вот мой код:
/*npm modules start*/
var MongoClient = require('mongodb').MongoClient;
/*npm modules end*/
// simple resouce limitation module, control parallel size
var simple_limit = require('simple_limit').simple_limit;
// one uri, one connection
var client_pool = {};
var default_options = {
server: {
auto_reconnect:true, poolSize: 200,
socketOptions: {
connectTimeoutMS: 1000
}
}
}
var mongodb_pool = function (uri, options) {
this.uri = uri;
options = options || default_options;
this.options = options;
this.poolSize = 10; // default poolSize 10, this will be used in generic pool as max
if (undefined !== options.server && undefined !== options.server.poolSize) {
this.poolSize = options.server.poolSize;// if (in)options defined poolSize, use it
}
}
// cb(err, db)
mongodb_pool.prototype.open = function (cb) {
var self = this;
if (undefined === client_pool[this.uri]) {
console.log('new');
// init pool node with lock and wait list with current callback
client_pool[this.uri] = {
lock: true,
wait: [cb]
}
// open mongodb first
MongoClient.connect(this.uri, this.options, function (err, db) {
if (err) {
cb(err);
} else {
client_pool[self.uri].limiter = new simple_limit(self.poolSize);
client_pool[self.uri].db = db;
client_pool[self.uri].wait.forEach(function (callback) {
client_pool[self.uri].limiter.acquire(function () {
callback(null, client_pool[self.uri].db)
});
})
client_pool[self.uri].lock = false;
}
})
} else if (true === client_pool[this.uri].lock) {
// while one is connecting to the target uri, just wait
client_pool[this.uri].wait.push(cb);
} else {
client_pool[this.uri].limiter.acquire(function () {
cb(null, client_pool[self.uri].db)
});
}
}
// use close to release one connection
mongodb_pool.prototype.close = function () {
client_pool[this.uri].limiter.release();
}
exports.mongodb_pool = mongodb_pool;