首页 > 解决方案 > 在 NodeJS 中防止 MongoDB 多个连接

问题描述

我看过一些关于这个主题的帖子;其中之一帮助我取得了进步——但我仍然有一个问题。

我的 Mongo 数据库充斥着连接并抛出错误。我确实有多个不同的连接,我正在使用客户名称建立这些连接。在网上我发现了这个有用的类:

export default class ConnectionManager {
static databases: any = {};

static getConnection(customer: string) : Promise<typeof mongoose> {
    if (this.databases[customer]) return Promise.resolve(this.databases[customer]);

    return new Promise<typeof mongoose>((resolve: any, reject: any) => {
        mongoose.connect(process.env.MONGOOSE_BASE_SERVER_URL + customer, { useNewUrlParser: true })
            .then((newDb: mongoose.Mongoose) => { 
                this.databases[customer] = newDb;
                resolve(this.databases[customer]);
            });
    });
  }
}

正如我所提到的,这很有帮助——如果连接已经存在,它就可以工作。

问题是我有一个计划任务,它可能会在现有连接超时后立即用请求淹没 API。如果此时 API 充满了请求——它们来得太快,打开了数百个连接,我仍然遇到同样的问题;在 ConnectionManager.databases 有机会使用新连接进行更新之前。

绞尽脑汁想办法避免这种情况。有人有建议吗?

谢谢,

编辑 - 认为这主要发生在服务重启后。这是一个示例错误:

{ Error: read ECONNRESET
    at TCP.onread (net.js:660:25)
  name: 'MongoNetworkError',
  errorLabels: [ 'TransientTransactionError' ],
  [Symbol(mongoErrorContextSymbol)]: {} }
{ MongoNetworkError: connection 202 to localhost:27017 closed
    at Socket.<anonymous> (.../node_modules/mongodb-core/lib/connection/connection.js:275:9)
    at Object.onceWrapper (events.js:273:13)
    at Socket.emit (events.js:182:13)
    at TCP._handle.close (net.js:599:12)
  name: 'MongoNetworkError',
  errorLabels: [ 'TransientTransactionError' ],
  [Symbol(mongoErrorContextSymbol)]: {} }

标签: node.jsdatabasemongodbhttpmongoose

解决方案


Promise 自然地提供缓存行为,因为已解决的 Promise 在链接时提供相同的结果。

一种正确的方法是将承诺存储在 中databases,这样就不会出现导致多个同名连接的竞争条件,即databases[customer] = Promise.resolve(mongoose.connect(...)).

但这对于内部链接连接承诺的 Mongoose 来说是不需要的;连接对象可以只保存和使用。使用方法创建多个连接createConnection

此外,仅静态类是反模式。它可以是一个对象或只是一个函数:

export const _databases = {};

export const getConnection = (customer) => {
  if (!_databases[customer])
    _databases[customer] = mongoose.createConnection(
      process.env.MONGOOSE_BASE_SERVER_URL + customer,
      { useNewUrlParser: true }
    );

  return _databases[customer];
}

databases对象是公共出口没有明显的理由。它可以导出用于测试目的。


推荐阅读