【问题标题】:AWS Lambda recursive invocation works too good?AWS Lambda 递归调用效果太好了?
【发布时间】:2017-04-05 02:18:10
【问题描述】:

我们将 SQS 队列用于异步消息,并且需要 Lambda 函数对某些队列上的消息进行一些转换和记录。 经过大量研究后,我决定我将使用递归 Lambda 函数,因为消息并不是很关键,并且在两者之间使用 SNS 或 SWF 似乎过于复杂(我希望亚马逊很快会为 SQS 添加一个 Lambda 触发器)。

Lambda 函数的每个请求的最大执行持续时间应该是 300 秒(5 分钟),所以我想反复调用 Lambda,然后将 Cloudwatch 触发器设置为 5 分钟以重新触发Lambda 再运行 5 分钟。

但是 Lambda 只是继续运行(没有 Cloudwatch 触发器)。我昨天测试了它,当它一直超过 300 秒时我感到很惊讶,现在它已经运行了超过 24 小时......

那么,问题是,它为什么一直在运行? 我假设每次调用它 Lambda 都认为它是一个新请求。由于 SQS 长轮询超时为 20 秒,而且我还在超时后调用(如果没有新消息),它会继续作为新请求进行,对吗?

另外,如果我每隔 5 分钟添加一次 Cloudwatch 触发器,我会启动同一个 Lambda 函数的多个实例吗?

(是的,我知道我是按运行时间计费的,但它仍然比 EC2 实例便宜,即使是 24/7 运行)

编辑: 添加显示调用和递归运行的 Cloudwatch 日志:

15:48:22 开始 RequestId:ee3f71df-b001-11e6-a0d6-bffc6057d58c 版本:$LATEST

15:48:42 2016-11-21T15:48:42.188Z ee3f71df-b001-11e6-a0d6-bffc6057d58c 再次调用……又一次……

15:48:42 END RequestId:ee3f71df-b001-11e6-a0d6-bffc6057d58c

15:48:42 报告请求 ID:ee3f71df-b001-11e6-a0d6-bffc6057d58c 持续时间:20115.93 毫秒计费持续时间:20200 毫秒内存大小:128 MB 使用的最大内存:37 MB

15:48:42 开始 RequestId:fa443a44-b001-11e6-bea9-4fe2d7bd8fe7 版本:$LATEST

15:49:02 2016-11-21T15:49:02.386Z fa443a44-b001-11e6-bea9-4fe2d7bd8fe7 一次又一次地打电话...

15:49:02 END RequestId: fa443a44-b001-11e6-bea9-4fe2d7bd8fe7

15:49:02 报告请求 ID:fa443a44-b001-11e6-bea9-4fe2d7bd8fe7 持续时间:20156.93 毫秒计费持续时间:20200 毫秒内存大小:128 MB 使用的最大内存:37 MB

15:49:02 开始 RequestId:0647caad-b002-11e6-adc9-73ebc92281fd 版本:$LATEST

15:49:22 2016-11-21T15:49:22.601Z 0647caad-b002-11e6-adc9-73ebc92281fd 一次又一次地打电话...

15:49:22 END RequestId:0647caad-b002-11e6-adc9-73ebc92281fd

15:49:22 报告请求 ID:0647caad-b002-11e6-adc9-73ebc92281fd 持续时间:20179.49 毫秒计费持续时间:20200 毫秒内存大小:128 MB 使用的最大内存:37 MB

【问题讨论】:

  • 你说的递归是什么意思?您的 Lambda 函数是否调用了更多自身实例?听起来您有失控的递归,导致您的函数的许多实例正在运行。
  • 是的,它正在调用自己。脚本完成的可能性有 3 种,消息处理成功、处理消息时出错或 SQS 轮询超时 20 秒。在这三个场景中的每一个中,Lambda 在“死亡”之前都会再次调用自己,因此它会在新的请求中重新调用自己,因此它应该同时只是一个实例。
  • 如您所见,它为每个“START”获取一个新的 RequestId,因此它永远不会达到 300 秒,因为最大轮询超时仅为 20 秒。我只是想确认我的理论,这样我就不会并行处理多个 Lambda 函数......
  • 如果它在每次完成时都调用自己的一个新实例,那么您将始终有一个函数实例在运行。我不知道你为什么对此感到惊讶。向此函数添加 CloudWatch 事件触发器将导致多个实例同时运行,因为您将让函数本身创建新调用以及 CloudWatch 创建新调用。
  • 在这种情况下,调用 == 请求。每次调用都是一个新请求,它会创建一个具有新超时的全新 Lambda 函数实例。 (我有点简单化并且忽略了 Lambda 容器的重用,但这与这个问题并不真正相关)

标签: node.js amazon-web-services aws-lambda


【解决方案1】:

如果,正如您在 cmets 中所说,您的函数是自调用的,那么添加触发器将导致新的 lambda 链每隔几分钟启动一次。就目前而言,您当前的函数应该永远运行(直到崩溃)。

如果你想在 lambda 中实现群安全,你可以做一些事情:

  1. 创建一个由 cloudwatch 调用的触发器 lambda。这个 lambda 会做的就是检查某个地方的过期键(S3、SQS 或 elasticache 都可以用于此),以查看在最后 X 分钟内是否调用了另一个 lambda。如果没有,请启动一个新副本。

  2. 如果队列为空,则删除 lambda 中的递归。

  3. 两者都保留,但只允许最大递归深度。这与 #2 稍有不同,因为在较长的运行期间(即:当您在 SQS 中有数据时),您可以将其设置为多个 lambdas 同时运行并等待空队列。但是,它也降低了产生无限 lambdas 的小错误的可能性。

独立于此,我认为有一个没有递归深度硬上限的递归 lambda 听起来超级危险。

【讨论】:

    猜你喜欢
    • 2016-07-06
    • 2015-11-15
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2011-04-30
    相关资源
    最近更新 更多