2013-03-28 55 views
21

我使用10gen的native node.js驱动器一起尝试使用mongodb(2.2.2)的node.js。处理Mongodb连接的正确方法是什么?

起初一切进展顺利。但是当进入并发基准测试部分时,出现了很多错误。频繁连接/接近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连接。但不知道为什么会发生这种情况。

并发测试代码:

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

回答

22

由于Node.js的是单线程你不应该被打开和关闭对每个请求的连接(如你会在其他多线程环境中执行)。

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

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

+0

虽然Node.js的使用一个单一的过程和事件模型,但IO动作应该永远是异步。我认为这可能不是我的问题的原因。但是,我会尝试你引用的方式,谢谢。 – Jack

+2

在第一个例子中,你错过了这一点;该代码相当于启动您的Mongo应用1000次。您应该只在应用中调用.connect一次。 –

+0

是的,这是问题,原因是MongoClient拥有一个连接池本身,它比其他dbs更昂贵。但不是node.js单一过程模型的问题。 – Jack

3

寻找到Hector的提醒后。我发现Mongodb的连接与我曾经使用的一些其他数据库完全不同。主要的区别是在的NodeJS本地驱动器:MongoClient都有它自己的连接池每个MongoClient开业,这池大小由

server:{poolSize: n} 

所以定义,poolSize开5 MongoClient连接:100,意味着总共5 * 100 =与目标Mongodb Uri的500个连接。在这种情况下,频繁开放的&关闭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; 
+0

你好,我遇到过这个问题。你可以通过'var simple_limit = require('simple_limit')。simple_limit; 'simple_limit.js代码?以及如何使用'mongodb_pool'?谢谢〜 – Eddy

相关问题