簡體   English   中英

何時關閉 Nodejs 中的 MongoDB 數據庫連接

[英]When to close MongoDB database connection in Nodejs

通過 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()來釋放資源? 還是需要打開一個新的數據庫連接?

這是一個基於計數方法的潛在解決方案(我沒有測試過它並且沒有錯誤捕獲,但它應該傳達這個想法)。

基本策略是:獲取需要更新多少條記錄的計數,異步保存每條記錄並在成功時回調,如果計數達到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);
        }
      });
    })
  });
});

最好使用池連接,然后在應用程序生命周期結束時在清理函數中調用 db.close() :

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

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

有點舊的線程,但無論如何。

我發現使用 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 的腳本,如果數據庫連接沒有正確關閉,該腳本將永遠掛在那里。

這是我想出的解決方案。 它避免使用 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();
    });
  });
});

我想出了一個涉及這樣的計數器的解決方案。 它不依賴於 count() 調用,也不等待超時。 在 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;
};

因此,每次您要進行 db.each() 或 db.save() 之類的調用時,您都可以使用這些方法來確保數據庫在工作時准備好並在完成時關閉。

來自 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. **
    }
  });
});

現在,這假設每個的倒數第二個回調在最后一個回調到 mydb.close() 之前通過 mydb.open() 。所以,當然,讓我知道這是否是問題。

所以:在 db 調用之前放置一個 mydb.open(db) 並在回調的返回點或 db 調用之后放置一個 mydb.close(db) (取決於調用類型)。

在我看來,這種計數器應該在 db 對象中維護,但這是我目前的解決方法。 也許我們可以創建一個新對象,它在構造函數中接受一個 db 並包裝 mongodb 函數以更好地處理關閉。

根據上面@mpobrien 的建議,我發現async模塊在這方面非常有用。 這是我采用的示例模式:

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

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

這是指向SIGINTSIGTERM 之間區別鏈接 我必須添加process.exit() ,否則在命令行中對正在運行的進程執行Ctrl + C時,我的節點網絡服務器不會干凈地退出。

無需計數器、庫或任何自定義代碼的現代方法:

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

在 mongodb 驅動程序的 3.2.7 版上對其進行了測試,但根據鏈接可能自2.0 版起有效

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM