何时关闭 Nodejs 中的 MongoDB 数据库连接

IT技术 javascript mongodb node.js asynchronous
2021-03-09 01:01:57

通过 Node MongoDB 本机驱动程序使用 Nodejs 和 MongoDB。需要检索一些文件,并进行修改,然后将它们保存回来。这是一个例子:

db.open(function (err, db) {
  db.collection('foo', function (err, collection) {
    var cursor = collection.find({});
    cursor.each(function (err, doc) {
      if (doc != null) {
        doc.newkey = 'foo'; // Make some changes
        db.save(doc); // Update the document
      } else {
        db.close(); // Closing the connection
      }
    });
  });
});

由于是异步的,如果更新文档的过程需要更长的时间,那么当光标到达文档末尾时,数据库连接就会关闭。并非所有更新都保存到数据库中。

如果db.close()省略,则所有文档都正确更新,但应用程序挂起,永不退出。

我看到一个帖子建议使用计数器来跟踪更新次数,当回落到零时,然后关闭数据库。但我在这里做错了什么吗?处理这种情况的最佳方法是什么?是否db.close()必须以释放资源?还是需要打开一个新的数据库连接?

6个回答

这是一个基于计数方法的潜在解决方案(我还没有测试过它并且没有错误捕获,但它应该传达这个想法)。

基本策略是:获取需要更新多少条记录的计数,异步保存每条记录,并在成功时回调,如果计数达到0(最后一次更新完成时),它将递减计数并关闭数据库。通过使用{safe:true}我们可以确保每次更新都成功。

mongo 服务器将为每个连接使用一个线程,因此最好 a) 关闭未使用的连接,或 b) 池/重用它们。

db.open(function (err, db) {
  db.collection('foo', function (err, collection) {
    var cursor = collection.find({});
    cursor.count(function(err,count)){
      var savesPending = count;

      if(count == 0){
        db.close();
        return;
      }

      var saveFinished = function(){
        savesPending--;
        if(savesPending == 0){
          db.close();
        }
      }

      cursor.each(function (err, doc) {
        if (doc != null) {
          doc.newkey = 'foo'; // Make some changes
          db.save(doc, {safe:true}, saveFinished);
        }
      });
    })
  });
});
@mpobrien,你能详细说明如何使用异步来解决这个问题吗?
2021-04-27 01:01:57
@realguess,还有用于并发工具的库可以帮助您完成这些工作,因此您不必管理细节。查看 async.js,例如github.com/caolan/async
2021-05-03 01:01:57
你认为这个解决方案在 2017 年仍然有效,或者你知道什么更好的吗?我正在考虑这样的事情,但是如果函数cursor.each(function (err, doc) {调用异步函数,这将在回调中执行逻辑并在each()完成后可能需要数据库呢?如果在软件中进行后续更改后,该回调调用另一个异步函数(我希望你明白)怎么办?
2021-05-16 01:01:57

最好使用池连接,然后在应用程序生命周期结束时在清理函数中调用 db.close() :

process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);

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

有点旧的线程,但无论如何。

@pkopac 在答案中的链接 - 对理解何时关闭 Nodejs 中的 MongoDB 数据库连接(这是被问到的问题)没有帮助。pkopac 在评论中的链接已损坏。尽管如此,我认为这是这里最好的答案,应该标记为已接受的答案......
2021-05-03 01:01:57
@ifightcrime:听起来像是一个正在运行的查询,而您已经关闭了连接。取决于您是否需要完成查询。如果您有需要等待的写入,我想您必须跟踪它们是手动完成的。您可以尝试在此处找到它的工作原理:github.com/mongodb/node-mongodb-native/blob/2.1/lib/db.js#L366
2021-05-06 01:01:57
这实际上给我带来了问题。有时,当我重新启动我的服务时,我会收到 Mongo“拓扑被破坏”错误,因为连接似乎被切断了。难道我做错了什么?
2021-05-14 01:01:57

我发现使用 counter 可能适用于简单的场景,但在复杂的情况下可能很难。这是我通过在数据库连接空闲时关闭数据库连接提出的解决方案:

var dbQueryCounter = 0;
var maxDbIdleTime = 5000; //maximum db idle time

var closeIdleDb = function(connection){
  var previousCounter = 0;
  var checker = setInterval(function(){
    if (previousCounter == dbQueryCounter && dbQueryCounter != 0) {
        connection.close();
        clearInterval(closeIdleDb);
    } else {
        previousCounter = dbQueryCounter;
    }
  }, maxDbIdleTime);
};

MongoClient.connect("mongodb://127.0.0.1:27017/testdb", function(err, connection)(
  if (err) throw err;
  connection.collection("mycollection").find({'a':{'$gt':1}}).toArray(function(err, docs) {
    dbQueryCounter ++;
  });   
  //do any db query, and increase the dbQueryCounter
  closeIdleDb(connection);
));

这可以是任何数据库连接的通用解决方案。maxDbIdleTime 可以设置为与 db 查询超时相同的值或更长时间。

这不是很优雅,但我想不出更好的方法来做到这一点。我使用 NodeJs 运行一个查询 MongoDb 和 Mysql 的脚本,如果数据库连接没有正确关闭,该脚本将永远挂在那里。

很有趣!
2021-04-30 01:01:57
简单快速的解决方案。谢谢
2021-05-03 01:01:57
嘿,我很欣赏答案,但是您需要将 clearInterval 从 closeIdleDb 更改为 checker :)。这真的帮助了我
2021-05-13 01:01:57

这是我想出的解决方案。它避免使用 toArray 并且它非常简短和甜蜜:

var MongoClient = require('mongodb').MongoClient;

MongoClient.connect("mongodb://localhost:27017/mydb", function(err, db) {
  let myCollection = db.collection('myCollection');
  let query = {}; // fill in your query here
  let i = 0;
  myCollection.count(query, (err, count) => { 
    myCollection.find(query).forEach((doc) => {
      // do stuff here
      if (++i == count) db.close();
    });
  });
});
如果最终写入数据库的其他异步调用在该// do stuff here部分中怎么办?他们不会发现它关闭了吗?
2021-04-19 01:01:57

这是pkopac 给出的答案的扩展示例,因为我必须弄清楚其余的细节:

const client = new MongoClient(uri);
(async () => await client.connect())();

// use client to work with db
const find = async (dbName, collectionName) => {
  try {
    const collection = client.db(dbName).collection(collectionName);
    const result = await collection.find().toArray()
    return result;
  } catch (err) {
    console.error(err);
  }
}

const cleanup = (event) => { // SIGINT is sent for example when you Ctrl+C a running process from the command line.
  client.close(); // Close MongodDB Connection when Process ends
  process.exit(); // Exit with default success-code '0'.
}

process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);

这是之间区别 链接我必须添加,否则我的节点网络服务器在命令行中执行正在运行的进程时不会干净地退出SIGINTSIGTERMprocess.exit()Ctrl + C