简体   繁体   English

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

[英]When to close MongoDB database connection in Nodejs

Working with Nodejs and MongoDB through Node MongoDB native driver.通过 Node MongoDB 本机驱动程序使用 Nodejs 和 MongoDB。 Need to retrieve some documents, and make modification, then save them right back.需要检索一些文件,并进行修改,然后将它们保存回来。 This is an example:这是一个例子:

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

With asynchronous nature, if the process of updating the document takes longer, then when cursor reaches the end of documents, database connection is closed.由于是异步的,如果更新文档的过程需要更长的时间,那么当光标到达文档末尾时,数据库连接就会关闭。 Not all updates are saved to the database.并非所有更新都保存到数据库中。

If the db.close() is omitted, all the documents are correctly updated, but the application hangs, never exits.如果省略db.close() ,则所有文档都正确更新,但应用程序挂起,永不退出。

I saw a post suggesting using a counter to track number of updates, when fall back to zero, then close the db.我看到一个帖子建议使用计数器来跟踪更新次数,当回落到零时,然后关闭数据库。 But am I doing anything wrong here?但是我在这里做错了什么吗? What is the best way to handle this kind of situation?处理这种情况的最佳方法是什么? Does db.close() have to be used to free up resource?是否必须使用db.close()来释放资源? Or does a new db connection needs to open?还是需要打开一个新的数据库连接?

Here's a potential solution based on the counting approach (I haven't tested it and there's no error trapping, but it should convey the idea).这是一个基于计数方法的潜在解决方案(我没有测试过它并且没有错误捕获,但它应该传达这个想法)。

The basic strategy is: Acquire the count of how many records need to be updated, save each record asynchronously and a callback on success, which will decrement the count and close the DB if the count reaches 0 (when the last update finishes).基本策略是:获取需要更新多少条记录的计数,异步保存每条记录并在成功时回调,如果计数达到0(最后一次更新完成时),它将递减计数并关闭数据库。 By using {safe:true} we can ensure that each update is successful.通过使用{safe:true}我们可以确保每次更新都成功。

The mongo server will use one thread per connection, so it's good to either a) close unused connections, or b) pool/reuse them. 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);
        }
      });
    })
  });
});

It's best to use a pooled connection and then call db.close() in cleanup function at the end of your application's life:最好使用池连接,然后在应用程序生命周期结束时在清理函数中调用 db.close() :

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

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

A bit old thread, but anyway.有点旧的线程,但无论如何。

I found that using counter may apply to simple scenario, but may be hard in complicated situations.我发现使用 counter 可能适用于简单的场景,但在复杂的情况下可能很难。 Here is a solution that I come up by closing the database connection when database connection is idle:这是我通过在数据库连接空闲时关闭数据库连接提出的解决方案:

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

This can be a general solution for any database Connections.这可以是任何数据库连接的通用解决方案。 maxDbIdleTime can be set as the same value as db query timeout or longer. maxDbIdleTime 可以设置为与 db 查询超时相同的值或更长时间。

This is not very elegant, but I can't think of a better way to do this.这不是很优雅,但我想不出更好的方法来做到这一点。 I use NodeJs to run a script that queries MongoDb and Mysql, and the script hangs there forever if the database connections are not closed properly.我使用 NodeJs 运行一个查询 MongoDb 和 Mysql 的脚本,如果数据库连接没有正确关闭,该脚本将永远挂在那里。

Here's a solution I came up with.这是我想出的解决方案。 It avoids using toArray and it's pretty short and sweet:它避免使用 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();
    });
  });
});

I came up with a solution that involves a counter like this.我想出了一个涉及这样的计数器的解决方案。 It does not depend on a count() call nor does it wait for a time out.它不依赖于 count() 调用,也不等待超时。 It will close the db after all the documents in each() are exhausted.在 each() 中的所有文档都用完后,它将关闭数据库。

var mydb = {}; // initialize the helper object.

mydb.cnt = {}; // init counter to permit multiple db objects.

mydb.open = function(db) // call open to inc the counter.
{
  if( !mydb.cnt[db.tag] ) mydb.cnt[db.tag] = 1;
  else mydb.cnt[db.tag]++;
}; 

mydb.close = function(db) // close the db when the cnt reaches 0.
{
  mydb.cnt[db.tag]--;
  if ( mydb.cnt[db.tag] <= 0 ) {
    delete mydb.cnt[db.tag];
    return db.close();
  }
  return null;
};

So that each time you are going to make a call like db.each() or db.save() you would use these methods to ensure the db is ready while working and closed when done.因此,每次您要进行 db.each() 或 db.save() 之类的调用时,您都可以使用这些方法来确保数据库在工作时准备好并在完成时关闭。

Example from OP:来自 OP 的示例:

foo = db.collection('foo');

mydb.open(db); // *** Add here to init the counter.**  
foo.find({},function(err,cursor)
{
  if( err ) throw err; 
  cursor.each(function (err, doc)
  {
    if( err ) throw err;
    if (doc != null) {
      doc.newkey = 'foo';
      mydb.open(db); // *** Add here to prevent from closing prematurely **
      foo.save(doc, function(err,count) {
        if( err ) throw err;
        mydb.close(db); // *** Add here to close when done. **
      }); 
    } else {
      mydb.close(db); // *** Close like this instead. **
    }
  });
});

Now, this assumes that the second to last callback from each makes it through the mydb.open() before the last callback from each goes to mydb.close().... so, of course, let me know if this is an issue.现在,这假设每个的倒数第二个回调在最后一个回调到 mydb.close() 之前通过 mydb.open() 。所以,当然,让我知道这是否是问题。

So: put a mydb.open(db) before a db call and put a mydb.close(db) at the return point of the callback or after the db call (depending on the call type).所以:在 db 调用之前放置一个 mydb.open(db) 并在回调的返回点或 db 调用之后放置一个 mydb.close(db) (取决于调用类型)。

Seems to me that this kind of counter should be maintained within the db object but this is my current workaround.在我看来,这种计数器应该在 db 对象中维护,但这是我目前的解决方法。 Maybe we could create a new object that takes a db in the constructor and wrap the mongodb functions to handle the close better.也许我们可以创建一个新对象,它在构造函数中接受一个 db 并包装 mongodb 函数以更好地处理关闭。

Based on the suggestion from @mpobrien above, I've found the async module to be incredibly helpful in this regard.根据上面@mpobrien 的建议,我发现async模块在这方面非常有用。 Here's an example pattern that I've come to adopt:这是我采用的示例模式:

const assert = require('assert');
const async = require('async');
const MongoClient = require('mongodb').MongoClient;

var mongodb;

async.series(
    [
        // Establish Covalent Analytics MongoDB connection
        (callback) => {
            MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
                assert.equal(err, null);
                mongodb = db;
                callback(null);
            });
        },
        // Insert some documents
        (callback) => {
            mongodb.collection('sandbox').insertMany(
                [{a : 1}, {a : 2}, {a : 3}],
                (err) => {
                    assert.equal(err, null);
                    callback(null);
                }
            )
        },
        // Find some documents
        (callback) => {
            mongodb.collection('sandbox').find({}).toArray(function(err, docs) {
                assert.equal(err, null);
                console.dir(docs);
                callback(null);
            });
        }
    ],
    () => {
        mongodb.close();
    }
);

Here an extended example to the answer given by pkopac , since I had to figure out the rest of the details:这是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);

Here is a link to the difference between SIGINT and SIGTERM .这是指向SIGINTSIGTERM 之间区别链接 I had to add the process.exit() , otherwise my node web-server didn't exit cleanly when doing Ctrl + C on the running process in command line.我必须添加process.exit() ,否则在命令行中对正在运行的进程执行Ctrl + C时,我的节点网络服务器不会干净地退出。

Modern way of doing this without counters, libraries or any custom code:无需计数器、库或任何自定义代码的现代方法:

let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://yourMongoDBUrl';
let database = 'dbName';
let collection = 'collectionName';

MongoClient.connect(url, { useNewUrlParser: true }, (mongoError, mongoClient) => {
   if (mongoError) throw mongoError;

   // query as an async stream
   let stream = mongoClient.db(database).collection(collection)
        .find({}) // your query goes here
        .stream({
          transform: (readElement) => {
            // here you can transform each element before processing it
            return readElement;
          }
        });

   // process each element of stream (async)
   stream.on('data', (streamElement) => {
        // here you process the data
        console.log('single element processed', streamElement);
   });

   // called only when stream has no pending elements to process
   stream.once('end', () => {
     mongoClient.close().then(r => console.log('db successfully closed'));
   });
});

Tested it on version 3.2.7 of mongodb driver but according to link might be valid since version 2.0在 mongodb 驱动程序的 3.2.7 版上对其进行了测试,但根据链接可能自2.0 版起有效

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM