AWS Lambda容器销毁事件

21

在Lambda中何时释放连接和清理资源。在正常的Node JS应用程序中,我们使用挂钩(hook)

process.on('exit', (code) => {
    console.log(`About to exit with code: ${code}`);
});

然而,在 AWS Lambda 上这种方法不起作用,导致 MySQL 连接处于睡眠模式。我们没有足够的资源来维持这样的活跃连接。AWS 的文档中没有指明如何实现这一点。

如何接收 AWS Lambda 容器的停止事件?


针对数据库连接问题,RDS Lambda代理是解决此问题的好方案。 - Steve D.
1个回答

23

简短的回答是,没有特定事件可以知道容器何时停止。

更新:我没有使用过这个库,但https://www.npmjs.com/package/serverless-mysql似乎试图解决这个问题。

以前的长回答:

中等回答:在与AWS的某位人员讨论后,我现在认为您应该在模块级别上限定数据库连接,以便在容器存在的同时重复使用它们。当您的容器被销毁时,连接也将在那时被销毁。

原始回答:

这个问题涉及到一些相当复杂的问题需要考虑 AWS Lambda 函数,主要是由于考虑连接池或长连接到您的数据库的可能性。首先,Node.js 中的 Lambda 函数执行为单个导出的 Node.js 函数,具有此签名(正如您可能已经了解的那样):

exports.handler = (event, context, callback) => {
    // TODO implement
    callback(null, 'Hello from Lambda');
};

处理数据库连接最干净、最简单的方法是在每个函数调用时创建和销毁它们。在这种情况下,一个数据库连接将在您的函数开始时创建,并在最终回调被调用之前被销毁。类似于这样:

const mysql = require('mysql');

exports.handler = (event, context, callback) => {
  let connection = mysql.createConnection({
    host     : 'localhost',
    user     : 'me',
    password : 'secret',
    database : 'my_db'
  });

  connection.connect();

  connection.query('SELECT 1 + 1 AS solution', (error, results, fields) => {
    if (error) {
      connection.end();
      callback(error);
    }
    else {
      let retval = results[0].solution;
      connection.end();
      console.log('The solution is: ', retval);
      callback(null, retval);
    }
  });
};

注意:我没有测试过这段代码。我只是提供了一个讨论的例子。

我也看到过像这样的对话,讨论将连接放在主函数体外的可能性:

const mysql = require('mysql');

let connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});

connection.connect();

exports.handler = (event, context, callback) => {
  // NOTE: should check if the connection is open first here
  connection.query('SELECT 1 + 1 AS solution', (error, results, fields) => {
    if (error) {
      callback(error);
    }
    else {
      let retval = results[0].solution;
      console.log('The solution is: ', retval);
      callback(null, retval);
    }
  });
};

这里的理论是: 因为 AWS Lambda 在第一次调用函数后会尝试重复使用现有容器,下一次函数调用将已经打开了一个数据库连接。上面的例子应该在使用之前检查是否存在打开的连接,但你明白了。

当然问题是这样会无限期地保持你的连接处于打开状态。我不喜欢这种方法,但根据你特定的情况,这可能有效。你也可以在这种情况下引入连接池。但无论如何,在这种情况下你没有事件清洁地销毁连接或连接池。托管您的函数的容器进程本身将被终止。因此,您必须依赖于数据库在某个时候从其端口关闭连接。

我可能对其中一些细节理解错误,但我相信在高层次上这就是您要考虑的内容。希望能帮到您!


1
我正在使用第二种方法。这就是为什么我需要关闭连接的原因。第一种方法会影响性能。在我们的应用程序中,每个请求都建立连接非常昂贵。有没有办法在第二种方法中关闭连接? - Viswanath Lekshmanan
3
我已经进行了减分,因为这个“答案”没有提供何时关闭连接的任何解决方案。 - Viktor Molokostov
12
@ViktorMolokostov 这个问题是关于如何接收 Lambda 容器的停止事件。答案是没有这样的事件。 - Todd Price
2
我在2020年底对第一种和第二种方法之间的差异进行了一些最近的性能评估,并可以自信地说,在打开托管在AWS RDS上的v12 Postgres的DB连接,读取一些数据并关闭连接的简单情况下,第一种和第二种方法之间几乎没有性能损失。在此答案中使用第一种方法,在读取之前打开连接,在函数退出之前在finally中关闭连接。进行快速性能检查即可完成!@ViktorMolokostov - Tim Fulmer
@TimFulmer 我认为从比较使用方法A(在函数范围内创建-销毁)和方法B(模块范围)的少数执行时间得出结论是危险的。要真正了解发生了什么,我怀疑您需要进行负载测试,其中会同时发生越来越多的执行。我预计方法B在规模上表现更好。此外,在这些测试期间查看DB指标以查看其在每种情况下的性能。您还可能需要尝试限制功能执行以限制并发性,以便不会有太多连接到数据库。 - Todd Price
显示剩余4条评论

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