在Node.js上连接MongoDB的最佳实践是什么?

38

这是对我来说有些不清楚的事情(我刚开始接触Node和Mongo),因为服务器性能和压力问题,这让我真正担心(我猜这又是另一个问题,但我会在文章结尾回答)。

所以,假设我正在使用Node.js和Restify编写API,其中每个API端点都对应一个函数,那么我应该:

a)打开db连接并将其存储在全局变量中,然后在每个函数中都使用它?
例如:

// requires and so on leave me with a db var, assume {auto_reconnect: true}
function openDB() {
    db.open(function(err, db) {
        // skip err handling and so on
        return db;
    }
}

var myOpenDB = openDB(); // use myOpenDB in every other function I have

b) 打开数据库连接,然后将所有内容放在一个大的闭包中吗?
示例:

// same as above
db.open(function(err, db) {
    // do everything else here, for example:
    server.get('/api/dosomething', function doSomething(req, res, next) { // (server is an instance of a Restify server)
        // use the db object here and so on
    });
}

c) 每次需要使用时打开和关闭数据库吗?
示例:

// again, same as above
server.get('/api/something', function doSomething(req, res, next) {
    db.open(function(err, db) {
        // do something
        db.close();
    });
});

server.post('/api/somethingelse', function doSomethingElse(req, res, next) {
    db.open(function(err, db) {
        // do something else
        db.close();
    });
});

这最后一个例子是我凭直觉会做的,但同时我并不完全舒服这样做。它不会对Mongo服务器造成太大的压力吗?特别是当它接收数百甚至数千个像这样的调用时(我希望我可以达到那个程度),你认为呢?

先行致谢。


1
这个问题与https://dev59.com/rmgv5IYBdhLWcg3wXPkt有关。 - César Alforde
2个回答

14

我非常喜欢MongoJS。它让你可以以与默认命令行非常相似的方式使用Mongo,并且它只是官方Mongo驱动程序的包装器。你只需要打开数据库一次并指定要使用的集合即可。如果使用--harmony-proxies参数运行Node,甚至可以省略集合。

var db = require('mongojs').connect('mydb', ['posts']);

server.get('/posts', function (req, res) {
  db.posts.find(function (err, posts) {
    res.send(JSON.stringify(posts));
  });
});

我真的很喜欢它!:D 非常感谢。但是,就像我在回答评论中对Gijs说的那样,我在某个地方读到,将所有操作都放在同一个连接下会导致阻塞代码。你知道这是否属实吗? - arturovm
我不这么认为。那样会失去意义。我相信与数据库的连接是通过套接字完成的。当使用流读取文件时,套接字在网络上执行与Node类似的操作。ReadStream以小块读取文件,并在接收到它们时触发事件。我认为套接字也会对网络块执行相同的操作。请参见http://nodejs.org/api/net.html#net_class_net_socket。 - juandopazo
阻塞或非阻塞取决于API。因此,上面的示例看起来像是阻塞的,因为在成功连接到数据库之前不会执行server.get(...)(这只是表面上的情况;我不熟悉MongoJS)。但是,考虑到您可能需要数据库才能执行任何操作,这并不一定是坏事,尽管如果您有许多需要很长时间才能执行的依赖项,则最好使用具有futures/promises/callbacks解决方案,就像@scttnlsn的解决方案中一样。 - Gijs
请注意,从MongoJS的Github页面上可以看出,它的实际查询实现似乎是异步的,因为它们都带有回调参数,所以在这方面不应该有任何问题。 - Gijs

6
  • 选项A并不是一个好主意,因为不能保证在处理HTTP请求之前DB已经打开完成(尽管这很不可能)
  • 选项C也不理想,因为它会无端地打开和关闭DB连接

我喜欢处理这个问题的方式是使用deferreds/promises。Node有很多不同的promise库可用,但基本思路是像这样做:

var promise = new Promise();

db.open(function(err, db) {
    // handle err
    promise.resolve(db);
});

server.get('/api/something', function doSomething(req, res, next) {
    promise.then(function(db)
        // do something
    });
});

我相信Mongoose以类似于以下方式处理连接。

谢谢。我研究了一下Mongoose,显然它与官方驱动程序的做法并没有太大区别。它只是在完成连接打开时触发一个事件,并且在未连接时缓冲操作,因此您不必担心它。不过我会再看看Promise这个东西 :) - arturovm
下面介绍的库MongoJS,实际上是使用Promise(尽管它们被称为futures)的更好的示例。 - scttnlsn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接