Mongoose: 断开连接后不要重新连接

创建于 2016-10-26  ·  47评论  ·  资料来源: Automattic/mongoose

从 4.4.11 升级到 >=4.6.1 后,我们会随机断开连接并且 mongoose 永远不会重新连接。

猫鼬版本:4.6.5
mongodb 版本:3.2.9

这是我创建连接的方式:

    var uri = 'mongodb://USENAME:PASSWORD<strong i="9">@host1</strong>:port1,host2:port2/database?authSource=admin';

    var options = {};
    options.server = {
      auto_reconnect: true,
      poolSize: 5,
      socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 },
      reconnectTries: 3000
    };

    options.replset = {
      auto_reconnect: true,
      poolSize: 5,
      socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 },
      reconnectTries: 3000
    };

    var db = mongoose.createConnection(uri, options);

    mongoose.connection.on('error', function(err) {
      console.log('MONGODB ERROR MONGOOSE LEVEL ' + server, err);
    });

    db.on('connecting', function() {
      console.info('MONGODB ' + server + ' connecting.');
    });

    db.on('error', function(err) {
      console.log('MONGODB ERROR ' + server, err);
    });

    db.on('close', function(err) {
      console.log('MONGODB CLOSE ' + server, err);
    });

    db.on('connected', function() {
      console.info('MONGODB ' + server + ' connected successfully.');
    });

    db.once('open', function callback() {
      console.info('MONGODB ' + server + ' opened successfully.');
    });

    db.on('reconnected', function() {
      console.info('MONGODB ' + server + ' reconnected.');
    });

    db.on('timeout', function() {
      console.info('MONGODB ' + server + ' timeout.');
    });

    db.on('disconnected', function() {
      console.info('MONGODB ' + server + ' disconnected');
    });

这是我得到的事件序列:

pid:3429 MONGODB geo_uri connected successfully.
pid:3429 MONGODB geo_uri opened successfully.
pid:3429 MONGODB dashboards_db connected successfully.
pid:3429 MONGODB dashboards_db opened successfully.
pid:3429 MONGODB tweet_analytics_db connected successfully.
pid:3429 MONGODB tweet_analytics_db opened successfully.
pid:3429 MONGODB fullcontact_enrichment_db disconnected
pid:3429 MONGODB ERROR fullcontact_enrichment_db { [MongoError: no valid replicaset members found]
  name: 'MongoError',
  message: 'no valid replicaset members found' }
pid:3429 MONGODB uri connected successfully.
pid:3429 MONGODB uri opened successfully.
pid:3429 MONGODB sync_reports_db connected successfully.
pid:3429 MONGODB sync_reports_db opened successfully.
pid:3429 MONGODB uri disconnected
pid:3429 MONGODB CLOSE uri

然后所有断开连接的数据库都不会重新连接。 由于问题的随机性,我将无法提供代码来重现它。 我怀疑某些东西在 node.js 级别左右饱和。 独立于断开连接的原因,我可以做些什么来尝试重新连接自己?

最有用的评论

@Koslun现在应该不需要分叉了。 现在的修复几乎只是将数据库 socketOptions 的 socketTimeout 设置为 0。

这是我的套接字选项现在的样子。

    var opts = {
      server: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
      },
      replSet: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
    }

所有47条评论

+1,在过去 5 天内有 2 次与此问题相关的中断。 我当前的解决方法是在发出disconnected事件时使我的进程显式崩溃( process.exit(0) )。 然后,重新启动进程并再次正确打开连接。

我的猜测是猫鼬尝试重新连接,但在reconnectTries * reconnectInterval间隔内失败,并且由于此回归(https://github.com/Automattic/mongoose/pull)不会发出error事件/4653)。 我不知道的是为什么 mongoose/mongo 会随机断开连接,我以前没有这种行为。
你在运行什么 mongo 主机? 实验室?

我们自己在 AWS 中托管它。 不幸的是, process.exit(0)不是我们的选择。

我在 #4653 处应用了更改并得到了相同的行为。 几个小时后断开连接:

2016-10-27T11:26:42 pid:5276 MONGODB sync_reports_db connected successfully.
2016-10-27T11:26:42 pid:5276 MONGODB sync_reports_db opened successfully.
.... 2 hours later
2016-10-27T13:45:45 pid:5276 MONGODB sync_reports_db disconnected
2016-10-27T13:45:45 pid:5276 MONGODB CLOSE sync_reports_db

没有发出error事件? (应该是, disconnected后 30 秒)

不,请查看问题描述中的代码。 除非我做错了什么,否则error事件有一个事件处理程序。 事实上,该进程仍在运行,猫鼬没有触发任何其他事件。

4.6.5以来的最后几天,我们一直遇到同样的问题——随机断开连接导致节点进程卡住。 但没有error事件。 恢复到4.5.3有效。

@loris这与4.6.6 https://github.com/Automattic/mongoose/commit/f7ebee0c992c45cdb27ba7f0675556b980cddaad有关吗?

@mck是的, https://github.com/Automattic/mongoose/commit/f7ebee0c992c45cdb27ba7f0675556b980cddaad修复了当 mongodb 连接重试机制失败时未发出的error事件。 但是,我不知道为什么首先会发生随机断开连接,知道@vkarpov15 吗?

fwiw,如果我们尝试执行保存/更新操作(写入约 650kb),它会在 40-50% 的时间发生在我们身上

是的,我真的没有很多好主意。 您可以尝试在连接上调用close() ,然后自己再次调用connect()@loris您是否有类似的经历,其中大量保存/更新似乎会导致这种情况?

我们也面临这个问题。 在我们的一项服务中,我们收到错误事件(连接 N 到 ds0XXXXX-a0.mongolab.com:XXXXX 超时),然后是断开连接事件。 并且连接永远不会重新建立。 在另一个服务上,我们在对数据库发出大量请求后收到断开连接事件,即删除 2M 记录。 然后它无法重新连接(猫鼬 4.6.6,DB 版本 3.0.12)。

几分钟前,它又发生在我们身上,也在 mLab 上运行 mongodb(不确定它是否相关)。 我们运行了一个繁重的查询,它超时了( unhandledRejection { MongoError: connection 0 to ds****-a0.mongolab.com:**** timed out }disconnected事件被正确发出,但之后什么也没有,没有error ,没有reconnected等,因此,该 Web 服务器继续运行并接收 HTTP 请求,但它们都超时,直到我们重新启动它,因为它一直在运行 mongoose 查询,这些查询已缓冲且从未返回。

我们的猫鼬设置(节点 7.0.0 上的 4.6.5):

const mongoConnectOpts = { reconnectTries: 10, reconnectInterval: 500, socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } };
mongoose.connect(process.env.MONGODB_URI, { server: mongoConnectOpts, replset: mongoConnectOpts });
mongoose.connection.on('error', err => {
  console.log({ event: 'mongoose:error', ...err });
  process.exit(0);
});
mongoose.connection.on('connected', () => console.log({ event: 'mongoose:connected' }));
mongoose.connection.on('disconnected', () => console.log({ event: 'mongoose:disconnected' }));
mongoose.connection.on('reconnected', () => console.log({ event: 'mongoose:reconnected' }));

临时解决方法是process.exit(0)也在disconnected事件上强制 Web 服务器重新启动并设置新的 mongodb 连接。 有任何想法吗?

是的,我以前听说过 mlab 发生这种事情。 TBH 在这种情况下,我只会使服务器崩溃并重新启动,非常缓慢的不稳定网络往往会给 mongodb 驱动程序带来麻烦,我想调试这将涉及与 mlab 的协调。

我不确定现在出了什么问题(mongoose、mongo 驱动程序、mlab 或 heroku),但在过去的几天里,运行执行繁重 mongoose 查询(需要 30 多秒才能响应)的网络请求将触发 heroku 请求超时(这是 heroku 中的一种机制,它使任何需要超过 30 秒的 Web 请求超时)。 一旦该 Web 请求超时,该服务器上需要 mongoose 查询的任何后续 Web 请求也将超时。 真正的问题是零事件是由猫鼬触发的(没有errorclosedisconnected等...),所以我们无法检测断开连接并重新启动服务器。 下面是我们如何设置猫鼬:

mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.NODE_ENV === 'development');
const mongoConnectOpts = { reconnectTries: 10, reconnectInterval: 500, socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } };
mongoose.connect(process.env.MONGODB_URI, { server: mongoConnectOpts, replset: mongoConnectOpts });
mongoose.connection.on('error', err => {
  logfmt.log({ event: 'mongoose:error', ...err });
  process.exit(0);
});
mongoose.connection.on('connected', () => logfmt.log({ event: 'mongoose:connected', uri: _.truncate(process.env.MONGODB_URI) }));
mongoose.connection.on('disconnected', () => {
  logfmt.log({ event: 'mongoose:disconnected' });
  process.exit(0);
});
mongoose.connection.on('close', () => logfmt.log({ event: 'mongoose:close' }));
mongoose.connection.on('reconnected', () => logfmt.log({ event: 'mongoose:reconnected' }));

// Setup Redis cache (Default cache TTL: 60 seconds)
cachegoose(mongoose, { engine: 'redis', client: redisCache }, process.env.NODE_ENV === 'development');

@Ioris你能看看如果禁用 cachegoose 是否还会发生这种情况吗?

@vkarpov15将尝试检查这一点,但并不容易,因为错误只发生在生产中,我们无法在禁用缓存的情况下运行生产。
@aartiles @mck- @lushchick你们使用猫鼬缓存插件吗?

我们不使用猫鼬缓存插件。

我还在研究它,到目前为止我能告诉你的是:

  • 我能够在我的本地机器上重现这个问题:我开始一个循环,每秒钟运行一个快速查询并记录结果,一些延迟之后,我运行一个繁重的查询(比如对一百万行集合的未索引查询)。 所有查询(快速查询)都将被锁定/超时,不会触发猫鼬事件。
  • 该问题出现在4.6.5 ,降级为4.6.4解决了该问题(在运行繁重查询时仍会执行快速查询)
  • 看起来它与以下无关:

    • 猫鼬缓存插件

    • 是否运行副本集

我做了更多的挖掘,问题是升级到[email protected] ,那里的错误提交是https://github.com/mongodb/node-mongodb-native/pull/1418
看起来他们修复了一个错字,但是某些 deps 正在使用 eventName(带有错字)

@loris你知道带有错误事件名称的剩余引用在哪里吗? 我可以做一个 PR 来修复它,但我找不到任何。

@jakesjews也找不到任何引用,所以我可能是错误的原因我可能在我的机器上检查了不匹配的版本,我会回复你

有一个类似的问题:断开连接后无法重新连接。 这通过挂起任何 mongoose db 操作(通过操作缓冲超时)在客户端中体现出来。

@jakesjews我的问题也是副本集独有的,并且不会发生在单节点连接中。 深入了解核心 replset.js。

在 node-mongodb-native 中启用调试日志记录以查看 HA 重新连接是否按预期工作,它似乎是。

attemptReconnect for replset with id successful resuming topologyMonitor 1

尽管 node-mongodb-native 驱动程序声称它成功执行了它的尝试重新连接,猫鼬永远不会像单个节点非 replset 重新连接时那样发出已连接或重新连接的事件。

正如@loris 所提到的,process.exit(0) -> 服务重启将起作用(在我的情况下),因为该问题与重新连接到副本集直接相关,但又不理想。

猫鼬@4.6.8
[email protected]

@mck- 发现和你一样,降级到 4.5.3修复了我的副本集连接的重新连接问题。

我可以一直升级到 4.5.10,然后它在 4.6.0 开始失败,其中第一次发生副本集重新连接问题。 目前猜测可能与升级到 mongodb 驱动程序 2.2.9 有关。

@loris能否提供您的测试用例以便我们试用?

我还在看它,以前我错了错字修复提交。 看起来罪魁祸首是https://github.com/christkv/mongodb-core/pull/146/commits/09caa9d1e5423acd2f8f154f7b7430028e77e57f
提供测试用例有点复杂,因为它只会以这种方式发生:

  • mongoose 4.6.8 ,使用默认设置连接到我的本地主机 mongodb (3.2)
  • 2 条快速路由,一条运行长时间运行的 mongoose 查询(几秒钟),一条运行快速运行的 mongoose 查询(直接在节点中运行 mongoose 查询时不会出现该问题,例如 setInterval/setTimeout 测试用例,所以我的猜测是它与池连接的处理方式有关)
  • 如果我执行长跑的快车路线,然后尝试打快跑的,后者会一直跑而不返回
  • poolSize50而不是默认值,解决问题
  • 检查mongodb-core的先前提交也解决了该问题(快速运行的快速路由在处理长时间运行的路由时在几毫秒内返回)(使用默认池大小)
  • 所以我猜https://github.com/christkv/mongodb-core/pull/146/commits/09caa9d1e5423acd2f8f154f7b7430028e77e57f改变了单个长时间运行的猫鼬如何使用池中的每个可用连接

一个修复刚刚登陆蒙戈核心可能解决这个问题。

@loris是的,增加池大小会有所帮助。 看起来您遇到了慢车问题,其中 mongodb 服务器无法同时处理超过poolSize请求。 增加池大小会有所帮助,只是不要增加太多,否则您将开始看到 WiredTiger 的性能问题。

你好,
这事有进一步更新吗? 我看到了同样的问题,使用副本集时 auto_reconnect 不起作用。 我刚刚用 mongodb 2.2.11 尝试了 mongoose 4.7.0,但仍然没有重新连接。 我正在使用 mongod 版本 3.2.10。

我在一台主机(笔记本电脑)上运行所有东西,三个 mongod 实例运行在具有不同数据库目录的不同端口上。 这似乎不是问题,但我是 mongo/mongoose/node/javascript 的新手。 我的带有 mongoose 的节点应用程序也在同一台主机上运行。

我可以通过简单地关闭所有 mongod 进程来重现这一点
(launchctl 停止 mongod01;launchctl 停止 mongod02;launchctl 停止 mongod03)
等待连接关闭消息,然后重新启动(在 launchctl 命令中将“停止”替换为“开始”)。 我的应用程序永远不会重新连接到 mongo。

如果我对未配置为副本集的 mongod 的单个实例进行相同的测试,则 mongoose 重新连接正常。

如果有帮助,很高兴提供日志或尝试补丁。

在做了一些挖掘之后,我想我可能已经找到了问题的一个根源。 看起来当 autoreconnect 为真时,连接缓冲区不应该变为活动状态https://github.com/Automattic/mongoose/blob/master/lib/drivers/node-mongodb-native/connection.js#L153在连接上关闭事件。 但是,在 mongodb-native https://github.com/mongodb/node-mongodb-native/blob/2.2/lib/replset.js的 replset 类中没有设置更多的 autoReconnect 属性,因此节点之一的任何关闭事件导致缓冲区永久启用。 我在提交https://github.com/eflexsystems/mongoose/commit/5ac12727f34b41791f94643b66c8cc88aff4d66a 时很幸运,但我想在我提出拉取请求之前给它更多时间看看它是否导致了任何其他问题。

@joeldodson您描述的问题与我遇到的相同。 只是一个提示 >= 4.6.0 似乎包含这个问题。 同时我会尝试 4.5.10,它已经重新连接到一个 replset 和单个连接对我来说很好。

谢谢@jakesjews@kog13

我尝试了 4.5.10 并且 mongoose 在重新启动副本集后重新连接。 然而,在副本集中的所有实例都停止后,db.readyState 似乎并没有被清除。 我们正在检查以确定是否拒绝任何请求(因此他们不会排队等待应用程序重新连接到数据库)。 此外,我在 4.5.10 中没有收到断开连接或连接关闭的通知。

如果应用程序启动时数据库不可用,我已经有一个逻辑,它位于一个带有 5 秒计时器的循环中,以尝试连接。 我们已经尝试在 db.on('closed', function(){...}) 处理程序中调用它,它似乎工作正常。 但我担心的是,明确尝试连接是否会与引擎盖下的任何重新连接逻辑产生任何冲突。 由于副本集似乎没有重新连接,所以我认为没关系。 此外,我们在服务器和 replset 的连接选项中将 auto_reconnect 设置为 false。

@jakesjews - 我尝试了你上面提到的补丁,但仍然没有重新连接。 也许我错过了一些东西,但看起来这个补丁更多的是确保生成关闭事件并更新 readyState。

如果有人有任何补丁,我很高兴为 auto_reconnect 尝试更多补丁。 我也会继续挖掘。

谢谢。

@joeldodson除了上面的补丁之外,您还需要依赖 mongo-core 上的最新版本,该版本具有确保副本集监视器连接保持活动状态的修复程序。 如果您试用我的叉子,它应该已经有了。

我认为我现在在猫鼬的超时和故障转移方面处于一个很好的位置。 如果其他人想尝试一下,除了在 socketOptions 中将 socketTimeout 设置为 0 之外,您还需要使用的 mongoose 分支。

将 socketTimeout 设置为 0 的原因是 mongo-core 中存在一个错误,我仍然需要为其提交问题。 该问题是由动态收缩/扩展连接池引起的。 池的工作方式是每次添加新连接时,连接将在 30 秒不活动后关闭。 超时事件会触发从池中删除,并且还会运行检查,将超时次数与 30 次连接尝试的限制进行比较。 每 2 秒运行一次心跳并清除超时次数,但如果 30 个或更多请求并行排队,它将导致所有请求在心跳之间超时并破坏连接池。 目前,将连接超时设置为 0 可防止连接在非活动状态时被修剪回池中并避免该问题。 如果您想复制该问题,请尝试将连接池大小设置为 50 左右并运行 50 个并行查询。 之后,池将在大约 30 秒内销毁。 请注意,超时不会影响检查副本集的心跳,因为它有自己的超时。

我最近真的忙于工作,所以我没有机会收集所有这些更改,但我希望尽快完成。

再次感谢@jakesjews。 我引入了你的 mongoose 和 mongodb-core 存储库。 重新连接确实有效。 我没有得到“连接”和“重新连接”事件,尽管我在使用单个 Mongo 实例时得到了。 而且 readyState 似乎没有被重置,即使在重新连接后它仍然是 0。

我很高兴帮助测试或收集日志。

关于这个问题有什么新的吗?

在这里也遇到问题。 由于连接问题,不得不恢复猫鼬版本升级。 当前需要几秒钟的查询使我们的连接超时,而以前情况并非如此。

必须处理此问题,否则该软件包将无法使用。
当我有时间时,可能会尝试@jakesjews解决方案。 在此之前使用 4.4.X

如果问题是 mongo-core 的错误,那么这实际上不是 mongoose 的问题。 如果您在使用最新版本的 mongoose 时遇到问题,您可以在mongodb-core库中提交问题吗?

实际上这是mongoose的问题,因为它已更新为取决于有问题的mongodb-core版本。 也许mongoose应该恢复到mongodb-core的先前版本。

@jakesjews眼看[email protected]取决于[email protected] ,而这又取决于[email protected] ,从您的应用修复将是需要解决这个问题的所有看似没有副作用?

看看你的 fork,这个提交现在是 4.7.x 和/或 4.8.x PR 所需的唯一更改吗?

@Koslun现在应该不需要分叉了。 现在的修复几乎只是将数据库 socketOptions 的 socketTimeout 设置为 0。

这是我的套接字选项现在的样子。

    var opts = {
      server: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
      },
      replSet: {
        socketOptions: {
          keepAlive: 1,
          socketTimeout: 0
        }
    }

@jakesjews好的,感谢您的迅速澄清:)。

你知道我们会在哪个版本的 mongodb-core 中看到这个错误的修复吗? 或者有一个我们可以跟踪的问题、公关或提交?

关于@Koslun评论的任何更新?

此问题有任何更新吗?

该问题从 2016 年开始仍然未解决 :open_mouth:

我想知道这个问题是否仍然会发生在带有 mongodb 驱动程序 3.3.4 的 mongoose 5.x 和版本 4.x 的 MongoDB 服务器上? :思维:

有谁知道,如果重新连接成功时 reconnectTries 被重置。

Fox 实例,如果 reconnectTries 设置为 30,并且一旦断开连接 mongoose 尝试 5 次并且连接成功。
下次连接丢失时,重试的计数器是什么?
它会尝试重新连接 30 次吗?
还是25次?

@szabolcs-szilagyi 是的,但前提是您没有将useUnifiedTopologytrue

@bhaveshvyas007是的。 这是相关的代码

为后人:

如果您在没有useUnifiedTopology情况下运行 Mongoose 5.x,请阅读本指南以管理 MongoDB 连接

如果您使用useUnifiedTopology运行 Mongoose 5.x,则此问题不会影响您。

此页面是否有帮助?
0 / 5 - 0 等级