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

Jac*_*ack 21 mongodb node.js

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

起初一切都很顺利.但是当进入并发基准测试部分时,发生了很多错误.经常连接/关闭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
Run Code Online (Sandbox Code Playgroud)

此外,如果许多客户端在没有显式关闭的情况下关闭,则需要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()
            })
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

通用池解决方案:

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);
            })
        }
    })
})
Run Code Online (Sandbox Code Playgroud)

Hec*_*rea 23

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

这是编写MongoDB node.js客户端模块的人的引用:

"当你的应用程序启动并重用db对象时,你打开一次MongoClient.connect.它不是单独的连接池,每个.connect都会创建一个新的连接池.因此,一旦[d]重复使用所有请求,请打开它." - christkv https://groups.google.com/forum/#!msg/node-mongodb-native/mSGnnuG8C1o/Hiaqvdu1bWoJ

  • 你在第一个例子中忽略了这一点; 该代码相当于启动您的Mongo应用程序1000次.你应该只在你的应用程序中调用一次.connect. (2认同)