我目前正在测试一些代码如何抵制以下情况:
要做到这一点,我有以下代码使用官方驱动程序(在这里找到: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方法中使用的用户名变量实际上是在我的完整代码中声明的,我在这篇文章中添加的代码是一个缩减版本来说明结构和错误检查
答案 0 :(得分:11)
<强> UPD:强>
基于这篇文章,看起来他们已经部署了与我们在这里做的相同的修复。不确定这是否已经在npm(15.10.13)内。 https://github.com/mongodb/node-mongodb-native/issues/1092#ref-commit-2667d13
经过一番调查后,我已经设法理解那里发生了什么:
每次调用任何方法处理数据库(查找,更新,插入等)时,它都会创建游标,该游标具有自己的ID并将自身注册到Db的EventEmitter,以便稍后回调。同时,它将自身注册到同一个CallBackStore中的_notReplied对象。
但是一旦连接关闭,我找不到任何会迭代_notReplied游标的东西,并会触发错误或任何带定时器的逻辑(它仍然可能在某处)。所以我设法写了一些小工作,当数据库发出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游标并触发错误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我们会看到他们对此有何看法。
答案 1 :(得分: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;
首先启动服务器并启动它()
然后你可以要求它并使用
mongo.getdb(function(err, db) {
if (err) {
console.log(err);
} else {
db.collection('user').find({'xxx':'xxx'}).toArray(function(err, items) {
console.log(items);
});
}
});
答案 2 :(得分:0)
经过进一步调查后,您似乎无法指定“离线”超时,例如上面列出的情景。可以指定的唯一超时是在10分钟不活动后通知服务器超时光标的超时,但是在上面的场景中,与服务器的连接已关闭,这不起作用。
作为参考,我在这里找到了这些信息:https://github.com/mongodb/node-mongodb-native/issues/987#issuecomment-18915263我认为谁是该项目的主要贡献者之一。
答案 3 :(得分:0)
我正在和Hapi和Mongodb(没有猫鼬)做api。特性:
结合其他答案的一些想法和这篇文章https://productbuilder.wordpress.com/2013/09/06/using-a-single-global-db-connection-in-node-js/我的方法就是:
server.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');
});
});
Utilities.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连接导出到全局空间。这感觉不是最好的解决方案,但我有一些项目,其中数据库连接作为参数传递给所有模块,并且吸引了更多。也许应该有一些模块化的方法,你需要它导入数据库连接,但在我的情况下,我几乎无处不在,我必须在大多数文件中写入include语句。这个API与db无关,没有连接,所以我认为这可能是最好的解决方案,即使我反对在全球空间神奇地飞行......