为啥建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?

Posted

技术标签:

【中文标题】为啥建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?【英文标题】:Why is it recommended not to close a MongoDB connection anywhere in Node.js code?为什么建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接? 【发布时间】:2013-01-07 21:24:40 【问题描述】:

考虑以下是 Node.js 代码:

function My_function1(_params) 
    db.once('open', function (err)
     //Do some task 1
);


function My_function2(_params) 
    db.once('open', function (err)
     //Do some task 2
);

查看最佳实践链接,该链接表示不要关闭任何连接

https://groups.google.com/forum/#!topic/node-mongodb-native/5cPt84TUsVg

我看到日志文件包含以下数据:

Fri Jan 18 11:00:03 Trying to start Windows service 'MongoDB'
Fri Jan 18 11:00:03 Service running
Fri Jan 18 11:00:03 [initandlisten] MongoDB starting : pid=1592 port=27017 dbpath=\data\db\ 64-bit host=AMOL-KULKARNI
Fri Jan 18 11:00:03 [initandlisten] db version v2.2.1, pdfile version 4.5
Fri Jan 18 11:00:03 [initandlisten] git version: d6...e0685521b8bc7b98fd1fab8cfeb5ae
Fri Jan 18 11:00:03 [initandlisten] build info: windows sys.getwindowsversion(major=6, minor=1, build=7601, platform=2, service_pack='Service Pack 1') BOOST_LIB_VERSION=1_49
Fri Jan 18 11:00:03 [initandlisten] options:  config: "c:\mongodb\mongod.cfg", logpath: "c:\mongodb\log\mongo.log", service: true 
Fri Jan 18 11:00:03 [initandlisten] journal dir=/data/db/journal
Fri Jan 18 11:00:03 [initandlisten] recover begin
Fri Jan 18 11:00:04 [initandlisten] recover lsn: 6624179
Fri Jan 18 11:00:04 [initandlisten] recover /data/db/journal/j._0
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:59343 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:118828 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:238138 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:835658 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:955218 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:3467218 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:3526418 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:3646154 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section seq:3705844 < lsn:6624179
Fri Jan 18 11:00:04 [initandlisten] recover skipping application of section more...
Fri Jan 18 11:00:05 [initandlisten] recover cleaning up
Fri Jan 18 11:00:05 [initandlisten] removeJournalFiles
Fri Jan 18 11:00:05 [initandlisten] recover done
Fri Jan 18 11:00:10 [initandlisten] query MYDB.system.namespaces query:  options.temp:  $in: [ true, 1 ]   ntoreturn:0 ntoskip:0 nscanned:5 keyUpdates:0  nreturned:0 reslen:20 577ms
Fri Jan 18 11:00:10 [initandlisten] waiting for connections on port 27017
Fri Jan 18 11:00:10 [websvr] admin web console waiting for connections on port 28017
Fri Jan 18 11:01:10 [PeriodicTask::Runner] task: WriteBackManager::cleaner took: 32ms
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50076 #1 (1 connection now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50077 #2 (2 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50078 #3 (3 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50079 #4 (4 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50080 #5 (5 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50081 #6 (6 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50082 #7 (7 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50083 #8 (8 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50084 #9 (9 connections now open)
Fri Jan 18 13:36:27 [initandlisten] connection accepted from 192.168.0.1:50085 #10 (10 connections now open)
...........................................
Fri Jan 18 13:36:48 [initandlisten] connection accepted from 192.168.0.1:50092 #97 (97 connections now open)

这不会通过打开多个连接而不是关闭它来增加服务器开销吗?它是否在内部处理连接池?

但在MongoDB Docs 中提到“这是不使用请求池的应用程序的正常行为”

谁能帮我理解这个。

【问题讨论】:

即使这个链接也说“保持一个或多个连接打开并在你的代码中重用它们”。 (在最后一条评论中)github.com/mongodb/node-mongodb-native/issues/84 【参考方案1】:

您使用 MongoClient 打开一次 Db 连接,然后在您的应用程序中重用它。如果您需要使用多个数据库,您可以使用 Db 对象上的 .db 函数来使用相同的底层连接池处理不同的数据库。保留一个池以确保单个阻塞操作不会冻结您的 node.js 应用程序。如果池中有 5 个连接,则默认大小。

http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

我也忘了补充。正如另一个答案指出的那样,建立一个新的 TCP 连接在时间和内存方面都是昂贵的,这就是你重用连接的原因。此外,一个新的连接也会导致在 MongoDB 上使用 Db 上的内存创建一个新的线程。

【讨论】:

我刚刚构建了每次都重新连接到 mongo 的 cron 任务。归档一些东西是一项快速的任务。每次重新连接任务大约需要 15-25 毫秒。重新使用连接需要〜0-1ms。这就是现实世界的差异:重用连接时速度增加 15-25 倍。当然有些人可能会说 25ms 已经足够快了,但是为什么即使在简单的任务上也要消耗更多的资源呢?只需重用连接。完成。【参考方案2】:

MongoDB 将数据库连接池化以提高效率,因此在 mongodb.log 中打开许多连接并不罕见

但是,当您的应用完全关闭时关闭所有连接会很有用。此代码最适合执行此操作。

process.on('SIGINT', function() 
  mongoose.connection.close(function () 
    console.log('Mongoose disconnected on app termination');
    process.exit(0);
  );
);

【讨论】:

什么是“过程”? 是node的原生方法 所以高效严谨的做法是:[1]打开一个连接; [2] 对所有数据库操作使用相同的连接,直到脚本被安排退出; [3] 仅当脚本计划退出时才关闭连接。我是否理解正确? @DavidEdwards 关于这个问题,你有没有得到任何地方的确认?我也有同样的想法,但不确定它是否 100% 正确 @eugensunic ...我发现,如果您无法在 node.js 脚本中关闭 MongoDB 连接,则即使在完成执行后,该脚本仍会保留在 Node 的滴答队列中。结果是服务器一直在等待 Node 完成,并且因为这永远不会发生,您最终会在 AJAX 中超时失败。【参考方案3】:

我不是 node.js 专家,但我认为大多数语言之间的原因是相对相同的。

建立连接是:

驱动程序所做的最重要的事情之一。正确设置连接可能需要数百毫秒,即使在快速网络上也是如此。

(http://php.net/manual/en/mongo.connecting.pools.php)

如果这是针对 PHP 的,并且文档有点过时,那部分即使现在仍然适用,并且适用于大多数(如果不是全部)驱动程序。

每个连接也可以使用单独的线程,这会导致明显的开销。

好像来自:

http://mongodb.github.com/node-mongodb-native/driver-articles/mongoclient.html#the-url-connection-format

那个 node.js 仍然使用连接池来尝试停止建立连接的开销。当然,这不再适用于 PHP 等其他驱动程序。

它会打开x 到您的数据库服务器的连接数量(默认为5),并在需要数据时将工作转移到空闲连接,因此重用旧连接可以避免这个可能导致这些日志的讨厌过程:

https://docs.mongodb.com/manual/faq/diagnostics/#why-does-mongodb-log-so-many-connection-accepted-events

【讨论】:

以上是关于为啥建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 node.js 不能在共享主机上运行? [关闭]

为啥异常会导致 Node.js 中的资源泄漏?

为啥 IndexedDB 在 node.js 中不可用? [关闭]

为啥 Node.js 是单线程的? [关闭]

为啥 Node.js 是单线程的? [关闭]

我应该使用哪个 Node.js 路由器? [关闭]