什么是正确的方式来处理Mongodb连接?

我使用10gen的native node.js驱动器一起尝试使用mongodb(2.2.2)的node.js。

起初一切顺利。 但是当来到并发基准testing部分时,出现了很多错误。 经常使用1000个并发连接/closures可能会导致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 

另外,如果很多客户端没有明确closures就closures,那么需要花费mongodb分钟来检测并closures它们。 这也会导致类似的连接问题。 (使用/var/log/mongodb/mongodb.log来检查连接状态)

我已经尝试了很多。 根据手册,mongodb没有连接限制,但poolSize选项似乎对我没有任何影响。

因为我只在node-mongodb-native模块中使用过它,所以我不太确定最终导致问题的原因。 其他语言和驱动程序的性能呢?

PS:目前,使用自我维护池是我想出的唯一解决scheme,但使用它不能解决与副本集的问题。 根据我的testing,副本集似乎没有多less独立的mongodb连接。 但不知道为什么会发生这种情况。

并发testing代码:

 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() }) } }) } 

通用池解决scheme:

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

由于Node.js是单线程的,因此不应该在每个请求上打开和closures连接(就像在其他multithreading环境中那样)。

这是编写MongoDB node.js客户端模块的人的一句话:

“当您的应用启动并重新使用db对象时,您会打开一次MongoClient.connect。 这不是一个单独连接池每个.connect创build一个新的连接池。 因此,一旦在所有请求中重复使用,请打开它。“ – christkv https://groups.google.com/forum/#!msg/node-mongodb-native/mSGnnuG8C1o/Hiaqvdu1bWoJ

看着赫克托的build议。 我发现Mongodb的连接与我曾经使用过的其他数据库有很大不同。 主要区别在于nodejs中的本地驱动器:MongoClient为每个打开的MongoClient拥有自己的连接池,该池的大小由

 server:{poolSize: n} 

所以,用poolSize:100打开5个MongoClient连接,意味着总共有5 * 100 = 500个连接到目标Mongodb Uri。 在这种情况下,频繁的打开和closuresMongoClient连接肯定会给主机造成很大的负担,最终导致连接问题。 这就是为什么我摆在首位的原因。

但是由于我的代码已经被写入,所以我使用连接池来存储每个不同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;