MongoDB是否存在重新连接问题,或者是我的操作有误?

30

我正在使用nodejs和mongoDB,但我遇到了一些连接问题。

实际上是“唤醒”问题! 它可以很好地连接 - 非常快速,我通常对结果很满意。

我的问题: 如果我不使用连接一段时间(时间范围变化5分钟以上),它似乎会停顿。 我没有收到断开连接事件 - 它只是挂起。

最终,我会得到一个错误响应:Error: failed to connect to [ * .mongolab.com: * ] - ( * = masked values)

重新启动应用程序后,连接就可以正常工作了。 有时,如果我不重新启动应用程序,我可以刷新并重新连接。

这就是为什么我认为这是“唤醒”问题的原因。

代码大致概述:

我没有包含代码 - 我认为没有必要。 它可以工作(除了连接中断)

需要注意的事项: 只有一个“connect” - 我从未关闭它。 我从未重新打开过。

/* constants */

var mongoConnect = 'myworkingconnectionstring-includingDBname';


/* includes */

/* settings */

/* Schema */

var db = mongoose.connect(mongoConnect);

    /* Socketio */

io.configure(function (){
    io.set('authorization', function (handshakeData, callback) {

    });
});

io.sockets.on('connection', function (socket) {

});//sockets

io.sockets.on('disconnect', function(socket) {
    console.log('socket disconnection')
});

/* The Routing */

app.post('/login', function(req, res){  

});

app.get('/invited', function(req, res){

});

app.get('/', function(req, res){

});

app.get('/logout', function(req, res){

});

app.get('/error', function(req, res){

});

server.listen(port);
console.log('Listening on port '+port);

db.connection.on('error', function(err) {
    console.log("DB connection Error: "+err);
});
db.connection.on('open', function() {
    console.log("DB connected");
});
db.connection.on('close', function(str) {
    console.log("DB disconnected: "+str);
});

我尝试过各种配置,比如一直开着和关着 - 我认为,普遍共识是像我这样只打开一个包含所有内容的标签。

我尝试了一个连接测试器,它不断检查连接的状态...即使看起来一切都正常 - 问题仍然出现。

从一开始就遇到了这个问题。我一直使用MongoLab托管MongoDB。 这个问题在本地主机上似乎更严重。但是我在Azure和nodejit.su上仍然有这个问题。

既然每个地方都会发生 - 这一定是我、MongoDB或mongolab的问题。

顺便说一句,我在使用php驱动程序时也有类似的经历。(确认这是在nodejs上)

如果有人能提供帮助,甚至只是告诉我“这很正常”,那就太好了。

提前感谢

罗布


不确定MongoDB,但在大多数(传统数据库)中,最好根据需要打开/关闭连接(即每次访问时)而不是永远保持打开状态。数据库驱动程序通常会优化在幕后创建和管理连接池,因此这种(打开/关闭)倾向于非常快速。我很想听听有实际MongoDB经验的人对这个问题的看法。 - Hector Correa
1
谢谢Hector - 是的,我同意,但是无论我在哪里调查,都说要按照我现在的方式做(除非我理解有误)。 - rob_james
我在MongoHQ和MongoLab实例上都遇到了同样的问题——不仅是从我的Node应用程序,而且从mongo命令行客户端也是如此。 MongoHQ支持建议我在应用程序或驱动程序级别处理这个问题,通过在发生这种情况时重试。在应用程序级别处理这个问题非常麻烦,我很惊讶mongodb驱动程序或mongoose中没有处理这个问题。 - Mike M. Lin
2
有人解决了这个问题吗?我有完全相同的配置和问题。在本地主机上更糟糕,但在云中仍然发生。你基本上只能在重新启动后获得一个会话。在Azure上,MongoLab对我来说几乎无法使用:( - Steven Ickman
2
也遇到了这个问题,但是在私有托管解决方案中。似乎与Mongo连接和keepAlive有关。 - thesmart
请查看下面的解决方案...现在看起来非常稳定。 - rob_james
6个回答

31

更新:我们针对此话题的支持文章(本质上是此帖子的副本)已移至我们的连接故障排除文档

已知问题是Azure IaaS网络强制执行大约十三分钟的空闲超时(经验性确定)。我们正在与Azure合作,以查看是否可以使事情更加用户友好,但在此期间,其他人可以通过配置其驱动程序选项来解决此问题。

最大连接空闲时间

我们与Azure和客户合作中发现的最有效的解决方法是将最大连接空闲时间设置为四分钟以下。其思想是使驱动程序在防火墙强制要求之前重新使用空闲连接。例如,一个使用C#驱动程序的客户将MongoDefaults.MaxConnectionIdleTime设置为一分钟,解决了他们的问题。

MongoDefaults.MaxConnectionIdleTime = TimeSpan.FromMinutes(1);

应用程序代码本身没有变化,但现在驱动程序在幕后积极回收闲置连接。这将在服务器日志中显示出来: 应用程序的空闲期间会有大量的连接变化。

关于这种方法的更多详细信息,请参见相关的mongo-user线程SocketException using C# driver on azure

保持连接

您还可以通过某种类型的keepalive减少连接处于闲置状态的时间来解决此问题。除非您的驱动程序已经支持此功能(通常是利用TCP Keepalive),否则实现起来可能有些棘手。如果需要自己编写,请确保每隔几分钟从池中获取每个空闲连接并发出一些简单而廉价的命令,通常是一个ping

处理断开连接

即使没有激进的防火墙设置,断开连接也可能偶尔发生。在投入生产之前,您需要确保正确处理它们。

首先,请确保启用自动重新连接。如何执行因驱动程序而异,但当驱动程序检测到操作失败,因为连接不良时,打开自动重新连接会告诉驱动程序尝试重新连接。

但这并不能完全解决问题。您仍然需要处理触发重新连接的失败操作。自动重试失败的操作通常是危险的,特别是对于写入操作。因此,通常会抛出异常请应用程序处理。通常可以轻松重试读取操作,但应该仔细考虑重试写入操作。

下面的mongo shell会话演示了这个问题。mongo shell默认情况下启用了自动重新连接。我在名为stuff的集合中插入一个文档,然后查找该集合中的所有文档。然后我设置了一个30分钟的定时器,并再次尝试相同的查找。它失败了,但shell自动重新连接了,当我立即重试我的查找时,它按预期工作。

% mongo ds012345.mongolab.com:12345/mydatabase -u *** -p *** 
MongoDB shell version: 2.2.2 
connecting to: ds012345.mongolab.com:12345/mydatabase 
> db.stuff.insert({}) 
> db.stuff.find() 
{ "_id" : ObjectId("50f9b77c27b2e67041fd2245") } 
> db.stuff.find() 
Fri Jan 18 13:29:28 Socket recv() errno:60 Operation timed out 192.168.1.111:12345 
Fri Jan 18 13:29:28 SocketException: remote: 192.168.1.111:12345 error: 9001 socket exception [1] server [192.168.1.111:12345] 
Fri Jan 18 13:29:28 DBClientCursor::init call() failed 
Fri Jan 18 13:29:28 query failed : mydatabase.stuff {} to: ds012345.mongolab.com:12345 
Error: error doing query: failed 
Fri Jan 18 13:29:28 trying reconnect to ds012345.mongolab.com:12345 
Fri Jan 18 13:29:28 reconnect ds012345.mongolab.com:12345 ok 
> db.stuff.find() 
{ "_id" : ObjectId("50f9b77c27b2e67041fd2245") }

我们在这里提供帮助

当然,如果您有任何问题,请随时通过电子邮件support@mongolab.com与我们联系。我们将确保为您提供帮助。


1
您链接的支持文章已不再可用。 - Andrei Bârsan
抱歉,我已经更新了这篇文章。不过你没有错过任何内容。正如你所看到的,文档基本上就是这篇文章的内容,只是格式稍有不同。 - jared
1
好的!谢谢!很遗憾MaxConnectionIdleTime选项在PyMongo驱动程序中还不可用(但愿会有)。 - Andrei Bârsan
@jared,Node.js MongoDB原生驱动程序支持maxIdleTimeMS吗? - moriesta
根据驱动程序文档,似乎没有maxIdleTimeMS,但server.socketOptions.keepAlive可能是你要找的。 - jared

21

感谢大家的帮助 - 我已经成功解决了在本地主机和部署到实际服务器上的问题。

以下是我现在可用的连接代码:

var MONGO = {
    username: "username",
    password: "pa55W0rd!",
    server: '******.mongolab.com',
    port: '*****',
    db: 'dbname',
    connectionString: function(){
        return 'mongodb://'+this.username+':'+this.password+'@'+this.server+':'+this.port+'/'+this.db;
    },
    options: {
        server:{
            auto_reconnect: true,
            socketOptions:{
                connectTimeoutMS:3600000,
                keepAlive:3600000,
                socketTimeoutMS:3600000
            }
        }
    }
};

var db = mongoose.createConnection(MONGO.connectionString(), MONGO.options);

db.on('error', function(err) {
    console.log("DB connection Error: "+err);
});
db.on('open', function() {
    console.log("DB connected");
});
db.on('close', function(str) {
    console.log("DB disconnected: "+str);
});

我认为最大的变化是使用"createConnection"代替"connect" - 我以前用过这个,但也许现在选项有帮助了。这篇文章帮了我很多忙 http://journal.michaelahlers.org/2012/12/building-with-nodejs-persistence.html

如果我诚实地说,我不太确定为什么要添加那些选项 - 正如@jareed提到的,我也发现一些人成功地使用了"MaxConnectionIdleTime" - 但就我所看到的javascript driver没有这个选项:这是我试图复制行为的尝试。

目前为止还不错 - 希望这能帮助到某些人。

更新:2013年4月18日注意,这是一个使用了不同设置的第二个应用程序

现在我以为我已经解决了这个问题,但最近在另一个应用上,这个问题又出现了 - 连接代码相同。困惑!!!

然而,设置略有不同...

这个新应用程序在Windows系统上使用IISNode运行。一开始我并没有认为这很重要。

我读到在Azure上可能会有一些关于mongo的问题(@jareed),所以我把数据库移到了AWS上 - 问题仍然存在。

所以我又开始玩那个选项对象了,很多阅读。得出这个结论:

options: {
    server:{
        auto_reconnect: true,
        poolSize: 10,
        socketOptions:{
            keepAlive: 1
        }
    },
    db: {
        numberOfRetries: 10,
        retryMiliSeconds: 1000
    }
}

那比我的原始选项对象更加文化一些,但仍然不好。

现在,由于某种原因,我必须离开那个Windows电脑(可能与一个模块无法在其上编译有关) - 比花费另一个星期的时间来让它工作更容易移动。

所以我把我的应用程序移到了nodejitsu。惊讶的是我的连接保持活动状态!哇!

那么...这是什么意思...我不知道!我知道的是这些选项对于Nodejitsu似乎有效...对于我来说。

我相信IISNode使用某种“永远”脚本来保持应用程序处于活动状态。公平地说,如果应用程序不崩溃,就不会启动该脚本,但我认为必须不断刷新某种“应用程序周期”-这就是如何进行连续部署(ftp代码上载,无需重新启动应用程序)-也许这是一个因素;但现在只是我的猜测。

当然,所有这些现在意味着这个问题没有解决。它仍然没有解决。只是在我的设置中解决了。


有点古怪,这就是让我成功的原因:使用 mongoose.createConnection 而不是 connect。不管怎样,多谢了。 - o-o
@rob_james - 你链接的文章似乎缺少示例。你知道有没有其他备选的副本吗?谢谢。 - UpTheCreek
@UpTheCreek 抱歉,我没有。 - rob_james
我曾经使用过类似的设置,由于我的“零停机部署”(复制集群然后更改DNS),我最终在mongodb上拥有了数百个连接,这完全拖慢了我的生产环境。 现在正在尝试在SIGINT / SIGQUIT上清除连接,试图摆脱页面错误...mongodb连接地狱 - kroe

6

对于仍然遇到此问题的人,以下是一些建议:

  1. 确保您正在使用最新的Node.js MongoDB客户端。当从v1.2.x迁移到v1.3.10(截至今日的最新版本)时,我在这个领域注意到了显着的改进。

  2. 您可以将选项对象传递给MongoClient.connect。连接Azure和MongoLab时,以下选项适用于我:

    options = { db: {}, server: { auto_reconnect: true, socketOptions: {keepAlive: 1} }, replSet: {}, mongos: {} };

    MongoClient.connect(dbUrl, options, function(err, dbConn) { // your code });

  3. 请参阅此其他答案,其中我描述了如何处理“close”事件,该事件似乎更可靠。 https://dev59.com/nHrZa4cB1Zd3GeqP2na-#20690008


1

像这样启用auto_reconnect服务器选项:

var db = mongoose.connect(mongoConnect, {server: {auto_reconnect: true}});

您在此打开的连接实际上是一个包含5个连接的池(默认情况下),因此您可以直接连接并保持打开状态。我猜测您与mongolab的连接会间歇性地中断,这时您的连接就会失效。希望启用auto_reconnect可以解决这个问题。

好的,很酷。我会尝试一下并告诉你结果。我以为自动重连已经启用了?而且我没有收到任何断开连接的事件 - 我会试一下的。 - rob_james
1
不好意思,MongoDB一直在mongoLab上。但我的代码已经在本地、Azure和nodejit.su上托管了,也许是mongoLab的问题,只是我不敢相信如果这是常见问题他们还能提供服务。这就是为什么我认为问题可能出在我身上。 - rob_james
2
“auto_reconnect: true” 不是默认设置吗? - UpTheCreek
根据文档,似乎不是这样的。不确定为什么。 - JohnnyHK
@UpTheCreek @JohnnyHK 当使用Mongoose时,默认情况下auto_reconnecttrue:http://mongoosejs.com/docs/connections.html - Elad Nava

1
增加超时时间可能会有帮助。
  • "socketTimeoutMS":在超时之前,套接字发送或接收的时间。
  • "wTimeoutMS":它控制服务器等待满足写入关注的毫秒数。
  • "connectTimeoutMS":连接在毫秒级别上打开之前可以花费多长时间。

    $m = new MongoClient("mongodb://127.0.0.1:27017", array("connect"=>TRUE, "connectTimeoutMS"=>10, "socketTimeoutMS"=>10, "wTimeoutMS"=>10));

        $db= $m->mydb;
        $coll = $db->testData;
        $coll->insert($paramArr);
    

0

我曾经遇到过类似的问题,就是定期与MongoDB断开连接。做了两件事情解决了这个问题:

  1. 确保你的电脑从不休眠(否则会导致网络连接中断)。
  2. 绕过你的路由器/防火墙(或者正确配置它,但我还没有弄清楚如何做到这一点)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接