AWS Lambda 容器销毁事件

Posted

技术标签:

【中文标题】AWS Lambda 容器销毁事件【英文标题】:AWS Lambda Container destroy event 【发布时间】:2017-12-27 08:05:54 【问题描述】:

何时释放 lambda 中的连接和清理资源。在普通的 Node JS 应用程序中,我们确实使用了钩子

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

但这在 AWS Lambda 上不起作用。导致 mysql 连接处于睡眠模式。我们没有足够的资源来进行此类活动连接。 AWS 文档均未指定实现此目的的方法。

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

【问题讨论】:

为了专门解决数据库连接问题,RDS Lambda Proxies 是一个很好的解决方案。 【参考方案1】:

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

更新:我没有使用过这个库,但 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);
    
  );
;

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

我还看到了like this one 讨论将您的连接置于主函数体之外的可能性:

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 在第一次调用您的函数后会尝试重用现有容器,所以下一次函数调用将已经打开了一个数据库连接。上面的示例可能应该在使用之前检查是否存在打开的连接,但您明白了。

当然,问题是这会使您的连接无限期地打开。我不喜欢这种方法,但根据您的具体情况,这可能会奏效。您还可以在该场景中引入连接池。但无论如何,在这种情况下,您没有任何事件可以彻底销毁连接或池。托管您的函数的容器进程本身将被杀死。所以你必须依赖你的数据库在某个时候从它的末端终止连接。

我可能对其中一些细节有误,但我相信从高层次上讲,这就是您所看到的。希望对您有所帮助!

【讨论】:

我使用的是第二种方法。这就是为什么我需要关闭连接。第一种方法是牺牲性能。它在每个请求时建立连接,这在我们的应用程序中是非常昂贵的。有没有办法用第二种方法关闭连接? @ViswanathLekshmanan 我不知道有任何机制可以让您在函数执行本身之外可靠地关闭和销毁连接。我们不直接从 Lambda 连接到我们的数据库,而是在我们完全控制的 ECS(Docker 容器)上托管一个供内部使用的 API。 我已经减去了这个,因为“答案”没有提供关于何时关闭连接的任何解决方案。 @ViktorMolokostov 问题是关于如何接收 Lambda 容器的停止事件。答案是没有这样的事件。 我最近在 2020 年末对第一种和第二种方法之间的差异进行了一些性能评估,并且可以自信地说,在打开与 AWS RDS 上托管的 v12 Postgres 的数据库连接的简单情况下,读取一些数据并关闭连接,第一种和第二种方法之间几乎没有性能损失。使用此答案中的第一种方法,在阅读之前打开连接,并在函数退出之前在 finally 中关闭连接。快速进行性能检查并完成! @ViktorMolokostov

以上是关于AWS Lambda 容器销毁事件的主要内容,如果未能解决你的问题,请参考以下文章

如何将 AWS cloudwatch 事件添加到基于具有 terraform 的容器映像的 aws_lambda_function?

线程池,lambda表达式

如何将解组事件用于 go1.x 的 lambda 容器映像中 AWS 定义的类型,并提供为基础

主网001提案|销毁Lambda研发团队持有的7亿LAMB投票通过

C++11 lambda 创建销毁

为啥 Terraform 要销毁这个导入的 aws_organizations_organizational_unit?