18

我目前正在测试一些代码如何应对以下场景:

  • Node.js 应用程序启动并成功建立与 mongodb 的连接
  • 成功建立连接后,mongodb服务器死掉,所有后续请求都失败

为此,我得到了使用官方驱动程序的以下代码(在此处找到:https ://github.com/mongodb/node-mongodb-native ):

MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
app.get('/test', function(req, res) {
    db.collection('users', function (err, collection) {
        console.log(err);
        if (err) {
            // ## POINT 1 ##
            // Handle the error
        }
        else {
            collection.find({ 'username': username }, { timeout: true }).toArray(function(err, items) {
                console.log(err);
                if (err) {
                    // ## POINT 2 ##
                    // Handle the error
                }
                else {
                    if (items.length > 0) {
                        // Do some stuff with the document that was found
                    }
                    else {
                        // Handle not finding the document
                    }
                }
            }); 
        }
    });
});

});

由于在处理请求时 mongodb 服务器不再运行,我假设在我标记为 ## POINT 1 ## 或 ## POINT 2 ## 的点上,它会返回一个错误指示超时;然而,事实并非如此。

我尝试了许多不同的设置(包括您可以在此处看到的明确允许光标超时的设置),但是我似乎无法以任何方式启用它。在我尝试过的每个配置中,Node.js 只会继续等待 find() 操作回调,但它永远不会。

如果我在运行 mongodb之前启动 Node.js 应用程序,它会很好地捕获连接回调中的错误,但如果在此之后连接断开,它似乎不会以任何方式处理它。

是否有我遗漏的设置,或者在建立连接后无法检测到连接被终止?

编辑:为了清楚起见,find 方法中使用的用户名变量实际上是在我的完整代码中声明的,我在这篇文章中放置的代码是一个简化版本,用于说明结构和错误检查。

4

4 回答 4

10

UPD:
根据这篇文章,看起来他们已经部署了与我们在这里所做的相同的修复。不确定这是否已经在 npm (15.10.13) 内。https://github.com/mongodb/node-mongodb-native/issues/1092#ref-commit-2667d13

经过一番调查,我设法了解那里发生了什么:
每次调用任何方法来处理数据库(查找、更新、插入等)时,它都会创建游标,该游标具有自己的 ID 并将自身注册到 Db 的 EventEmitter因为稍后被召回。同时,它将自身注册到同一 CallBackStore 中的 _notReplied 对象。

但是一旦连接关闭,我就找不到任何会遍历 _notReplied 游标并会用错误或任何带有计时器的逻辑触发它们的东西(它仍然可能在某个地方)。因此,我设法编写了一些小工作,当 DB 发出close事件时,它确实会强制触发游标出错:

new mongodb.Db('testdb', new mongodb.Server('localhost', 27017, { }), { safe: true }).open(function (err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err)
  }
});

我建议使用第一种方法而不是 MongoClient。原因很少:例如,当您关闭连接然后调用.find它时会正确触发回调错误,而使用 MongoClient 则不会。

如果您使用的是 MongoClient:

MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err);
  }
});

这会做什么?一旦连接关闭,它将遍历所有 _notReplied 游标并为它们触发事件 error Connection Closed!

测试用例:

items.find({ }).toArray(function(err, data) {
  if (!err) {
    console.log('Items found successfully');
  } else {
    console.log(err);
  }
});
db.close();

这将强制关闭数据库连接并触发close您之前处理的事件,并确保将关闭游标。

UPD:我在 GitHub 上添加了问题:https ://github.com/mongodb/node-mongodb-native/issues/1092我们将看看他们对此有何评论。

于 2013-09-10T09:58:37.697 回答
1

我有同样的问题,并从谷歌找到了这个页面。但是你选择的答案并没有解决问题,它和你一样,this._callBackStore 不能使用

但我试图包裹 Mongo,它似乎工作正常

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

var mongo = {};
mongo.init = function() {
  MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
    if (err) {
      mongo.DB = '';
    } else {
      mongo.DB = db;
    }
    db.on('close', function() {
      mongo.DB = '';
    });
    db.on('reconnect', function() {
      mongo.DB = db;
    });
  }
}
                      
mongo.getdb = function(callback) {
    if (mongo.DB) {
      callback(null, mongo.DB);
    } else {
      callback('can not connect to db', null);
    }
}
module.exports = mongo;

首先启动服务器并 init() 它

然后你可以要求它并使用

mongo.getdb(function(err, db) {
  if (err) {
    console.log(err);
  } else {
    db.collection('user').find({'xxx':'xxx'}).toArray(function(err, items) {
      console.log(items);
    });
  }
});

于 2014-11-28T10:50:58.910 回答
0

经过进一步调查,您似乎无法指定“离线”超时,例如上述情况。唯一可以指定的超时是通知服务器在 10 分钟不活动后使光标超时,但是在上面的场景中,与服务器的连接断开,这不起作用。

作为参考,我在这里找到了信息:https ://github.com/mongodb/node-mongodb-native/issues/987#issuecomment-18915263 ,我认为他是该项目的主要贡献者之一。

于 2013-09-09T20:46:02.863 回答
0

我正在用 Hapi 和 Mongodb(不带猫鼬)​​制作 api。特征:

  1. 仅当 mongo db 可用时才开始响应 API 请求
  2. 如果 mongo 在循环期间死亡,则停止响应
  3. 当 mongo 再次可用时重新启动
  4. 为所有请求保持单一连接

结合其他答案的一些想法和这篇文章https://productbuilder.wordpress.com/2013/09/06/using-a-single-global-db-connection-in-node-js/我的方法是:

服务器.js

Utilities.initializeDb(() => {
    server.start((err) => {
        if (err) throw err;
        console.log('Server running at:', server.info.uri);
    });
}, () => {
    server.stop((err) => {
        if (err) throw err;
        console.log('Server stopped');
    });
});

实用程序.js

"use strict";

const MongoClient = require('mongodb').MongoClient;
const MongoUrl = 'mongodb://localhost:27017/db';

export const Utilities = {
    initializeDb: (next, onCrash) => {

        const ConnectToDatabase = (params) => {
            MongoClient.connect(MongoUrl, (err, db) => {
                if (err !== null) {
                    console.log('#t4y4542te Can not connect to mongo db service. Retry in 2 seconds. Try #' + params.retry);
                    console.error(err);
                    setTimeout(() => {
                        ConnectToDatabase({retry: params.retry + 1});
                    }, 2000);
                } else {

                    db.on('close', () => {
                        onCrash();
                        console.log('#21df24sf db crashed!');
                        ConnectToDatabase({retry: 0});
                    });
                    global.db = global.db || db;
                    next();
                }
            });
        };

        ConnectToDatabase({retry: 0});

    }
};

我正在将数据库连接导出到全局空间。感觉这不是最好的解决方案,但我有一些项目,其中 db 连接作为参数传递给所有模块,而且更糟糕。也许应该有一些模块化方法,您可以在需要的地方导入数据库连接,但在我的情况下,我几乎在任何地方都需要它,我必须在大多数文件中编写包含语句。这个 API 没有与 db 的连接毫无意义,所以我认为它可能是最好的解决方案,即使我反对让某些东西在全球空间中神奇地飞行..

于 2016-04-18T19:31:34.597 回答