每次调用新的 Lambda 时,Mongoose 连接池都会创建与 Mongodb 的连接

Ava*_*iya 11 connection-pooling mongoose mongodb node.js aws-lambda

我们使用 Mongoose、Nodejs、Serverless 和 AWS Lambda。为了使用相同的连接,而不是每次需要时打开和关闭连接,我创建了一个大小为 10 的连接池(这对于我们现在的用例来说似乎足够了)。

但问题是,当我看到 Lambda 的 Cloudwatch 日志时,发现所使用的连接并不相同。

每次调用新的 Lambda 时,都会创建一个新连接,而对该 Lambda 的后续调用将使用在第一次调用中打开的相同连接。

导致一次打开的连接数增加。在 MongoDB Atlas 上,我可以看到打开的连接数量非常多。

下面是我在没有可用缓存连接的情况下用于创建连接的代码。如果可用,将使用缓存的连接,并且不会创建新连接。

let cached_db;
exports.createConnection = async () => {
  if(cached_db == null){
    return await mongoose.connect(
    connection_uri,         
    { 'useUnifiedTopology': true , 
      'useNewUrlParser':  true, 
      'useFindAndModify': false , 
      'useCreateIndex': true,
      'socketTimeoutMS': 60000,
      'connectTimeoutMS': 60000,
      'poolSize': 10
    }
  ).then(conn => {
      cached_db = conn;
      return conn;
  }).catch((err) => {
      console.error('Something went wrong', err);
      throw err;
    });
  } else {
    console.log("Cached db in use.");
    return cached_db;
  }
}
Run Code Online (Sandbox Code Playgroud)

相同的连接可以跨 Lambda 使用吗?有办法做到吗?

Nen*_*vic 9

您应该在 AWS Lambda 处理程序函数之外定义 MongoDB 服务器的客户端。不要在每次调用函数时都定义新的 MongoClient 对象。这样做会导致驱动程序在每次函数调用时创建一个新的数据库连接。这可能成本高昂,并且可能导致您的应用程序超出数据库连接限制。

作为替代方案,请执行以下操作:

  1. 创建一次 MongoClient 对象。
  2. 存储该对象,以便您的函数可以跨函数调用重用 MongoClient。

步骤1

将对函数的调用隔离MongoClient.connect()到其自己的模块中,以便可以跨函数重用连接。让我们mongo-client.js为此创建一个文件:

mongo-client.js

const { MongoClient } = require('mongodb');

// Export a module-scoped MongoClient promise. By doing this in a separate
// module, the client can be shared across functions.
const client = new MongoClient(process.env.MONGODB_URI);

module.exports = client.connect();
Run Code Online (Sandbox Code Playgroud)

第2步

导入新模块并在函数处理程序中使用它来连接到数据库。

一些文件.js

const clientPromise = require('./mongodb-client');

// Handler
module.exports.handler = async function(event, context) {
  // Get the MongoClient by calling await on the connection promise. Because
  // this is a promise, it will only resolve once.
  const client = await clientPromise;
  
  // Use the connection to return the name of the connected database for example.
  return client.db().databaseName;
}
Run Code Online (Sandbox Code Playgroud)

泳池大小

连接池大小是维护的数据库连接的缓存,以便在将来需要向数据库发出请求时可以重用这些连接。连接池用于增强在数据库上执行命令的性能。

注意: maxPoolSizepoolSize是相同的,只不过它们与您是否正在使用该useUnifiedTopology: true设置有关。如果您使用useUnifiedTopology: truemaxPoolSize是符合规范的设置来管理连接池的大小。但是,如果您正在使用useUnifiedTopology: false(或省略它),则poolSize是相同的事情,但在我们拥有统一拓扑之前。

注意:每个连接大约消耗 1MB RAM。

池大小的值

连接池以每个 mongod/mongos 为基础,因此当连接到 3 成员副本时,将存在三个连接池(每个 mongod 一个),每个连接池都有一个maxPoolSize. 此外,每个节点还需要一个监控连接,因此最终会得到(maxPoolSize+1)*number_of_nodesTCP 连接。

在我看来,如果你不关心 CPU 和 RAM,你应该使用所有可用的连接(如果我们已经有了它们为什么不呢,对吧?)。

例如:您有一个包含 3 个副本集的 Atlas 免费集群,最多支持 500 个连接,并且您只有一个应用程序连接到该集群,请将所有连接都分配给该一个应用程序。为了设置 的值poolSize,您可以使用上面的连接计算:

poolSize = (maximum_connections/number_of_nodes) - 1

poolSize = (500/3) - 1

poolSize = 165
Run Code Online (Sandbox Code Playgroud)

如果您有 2 个应用程序将连接到同一集群,请为每个应用程序分配一半的连接。

如果您的 RAM 内存有限,请检查您可以使用多少内存并poolSize据此进行计算(正如我在注释中所说,您可以假设一个连接将消耗大约 1MB 的 RAM)。

资源

有关更多信息,请查看官方 MongoDB 文档

对于连接池,请检查thisthis