SQS Lambda - 重试逻辑?

27

当消息被添加到SQS队列中并配置为触发Lambda函数(node.js)时。

当Lambda函数被触发时,我可能想要在不从队列中删除消息的情况下再次重试相同的消息,等待5分钟。我之所以这样做是因为如果Lambda无法连接外部主机(例如API),我希望在3次尝试后再次尝试5分钟。

如何在node.js中编写这个?

例如在Laravel中,我们可以使用public $tries = 5;指定最大作业尝试次数的功能。

源自:https://laravel.com/docs/5.7/queues#max-job-attempts-and-timeout

我们如何在node.js中以类似的方式执行?

我正在考虑将消息添加到另一个队列(用于重试)。 Lambda函数在5分钟后从该队列中读取所有消息,并将该消息发送回主队列,然后触发Lambda函数。


https://www.lucidchart.com/blog/cloud/5-reasons-why-sqs-lambda-triggers-are-a-big-deal - Spiff
4个回答

68

重试和超时时间都可以直接在SQS队列中进行配置。

创建队列时,请设置以下属性:

SQS Queue Attributes

{{默认可见超时时间}}是指消息在被应用程序接收后将被隐藏的时间。如果消息在Lambda运行期间失败并抛出异常,则Lambda不会删除批处理中的任何消息,所有消息最终都将重新出现在队列中。
如果您只想尝试3次,则必须设置SQS重传策略(也称为死信队列)。

Dead Letter Queue Settings

重新驱动策略将使您的队列在消息重新出现次数达到N(其中N是1到1000之间的数字)后将消息重定向到死信队列(DLQ)。
需要了解的是,Lambda将继续处理失败的消息(即在代码中生成异常的消息),直到以下情况发生:
1. 消息被成功处理(Lambda将删除该消息) 2. 消息保留期到期(SQS将删除该消息) 3. 将其发送到SQS队列重新驱动策略中设置的DLQ(SQS“移动”消息到DLQ) 4. 直接在代码中从队列中删除消息(用户删除消息)
否则Lambda不会处理此错误消息。

重要观察

Lambda不会处理失败的消息

根据我运行的几个实验,以了解SQS集成的行为(文档中的重试说明可能不明确)。

Lambda不会删除失败的消息,并将继续重试它们。即使您设置了Lambda DLQ,失败的消息也不会发送到Lambda DLQ。正如Lambda DLQ文档所述,Lambda完全依赖于SQS队列的配置来实现此目的。

建议:

  • 始终在SQS队列中使用重新驱动策略。

异常将导致整个消息批次失败

如我之前所述,如果在处理消息时出现代码异常,则整个消息批次将被重试,无论某些消息是否已正确处理。如果由于某种原因下游服务失败,则可能会在DLQ中得到已处理的消息。

建议:

  • 手动删除已正确处理的消息
  • 确保您的Lambda函数可以多次处理同一条消息

Lambda并发限制和SQS副作用

这篇博客 "Lambda Concurrency Limits and SQS Triggers Don’t Mix Well (Sometimes)" 描述了如果您的并发限制设置过低,Lambda可能会导致一批消息被限流,并使得 接收尝试 增加而没有被处理。 建议: 博客和亚马逊的建议如下:
  • 将队列的可见性超时设置为至少比您在函数上配置的超时时间长6倍。
  • 额外的时间允许Lambda在函数处理前一批消息时重试,以防止函数执行被限流。
  • 将队列的重新发送策略中的maxReceiveCount设置为至少5。这有助于避免由于限流而将消息发送到死信队列。
  • 配置死信以保留失败的消息足够长的时间,以便稍后将其移回以进行重新处理。

非常好的解释和观察。谢谢您先生! - Aldee
1
您可以使用以下方式仅使批处理中的某些消息失败:https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting - Francisco Cardoso
不错,这是新的(在过去6个月内?)! - Onema

17

这是我的做法。

  1. 创建正常队列(立即传递),Q1
  2. 创建延迟队列(5分钟延迟),Q2
  3. 创建死信队列(重试后),DLQ1

(Q1/Q2) SQS 触发器 --> Lambda L1(如果失败,则在 (Q1/Q2) 上删除,将其放入 Q2)--> 失败时发送到 DLQ

当消息到达 Q1 时,它会触发 Lambda L1。如果成功,就从那里继续。如果失败,就把它丢到 Q2(一个延迟队列)。到达 Q2 的每条消息都会有 5 分钟的延迟。

如果你的初始消息可以有 5 分钟的延迟,那么你可能不需要两个队列。一个队列应该足够了。如果初始延迟不可接受,那么你就需要两个队列。另一个需要有两个队列的原因是,你总是有一种方式来处理新消息。

如果代码在处理 Q1/Q2 时出现故障,AWS 基础设施将在将其发送到 DLQ1 之前立即重试 3 次。如果你在代码中处理错误,则可以让管道按照你所提到的时间表工作。

SQS 延迟队列:

https://docs.aws.amazon.com/zh_cn/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html

SQS Lambda 架构:

Amazon SQS作为Lambda事件源

在此输入图片描述 希望这有所帮助。


1
是的。删除需要在Lambda代码中手动处理。您需要在第一次重试/成功时删除Q1,并在重试后删除Q2。在SQS负载中设置一个变量以指示重试次数。如果代码中有错误,它将到达DLQ。这需要使用不同的过程将消息从DLQ移动到Q2以进行进一步处理。 - Kannaiyan
2
我非常确定Lambda会在没有错误的情况下自动删除SQS消息。当您提到在SQS负载中设置一个变量以指示重试次数时,SQS是否使用“最大接收次数”选项进行重试? - I'll-Be-Back
3
Lambda负责:在Lambda函数成功完成后删除它们。 - I'll-Be-Back
1
使用VisibilityTimeout属性来间隔重试不是更好吗?据我所知,这样就不需要中介队列了。 - Julian Go
1
多队列方法的优点是它可以添加延迟。您还可以将消息推送到同一队列并添加延迟。DelaySeconds 是 sendMessage 的一个参数。否则,下一次重试消息的时间将在默认可见超时之后。 - Todd Hoff
显示剩余5条评论

4
如果以异步方式执行Lambda,那么这个过程非常简单,而且不需要编写任何代码。首先,如果您的代码出现错误,AWS Lambda将重试3次以执行您的代码。在这种情况下,如果外部API无法访问,则在AWS重试第三次时,API很可能会工作。此外,重新尝试之间的延迟是随机的,这意味着重新尝试之间存在延迟。
如果最坏的情况发生了,并且外部API还没有启动,您可以利用每个Lambda都有的死信队列(DLQ)功能。这将推送一条消息到SQS,说明出了什么问题,因此您可以采取其他措施。在这种情况下,请不断重试直到成功为止。
您可以在此处阅读更多信息:https://docs.aws.amazon.com/lambda/latest/dg/dlq.html

每次重试的时间是多少?我想在重试后5分钟再尝试。 - I'll-Be-Back
它是随机的,可能是10秒,也可能是第一次30秒,然后每次重试时间增加。但你无法控制它。而且你不能禁用它,所以每次出现错误Lambda都会这样做,不管你喜不喜欢。如果你想要使用DLQ,可以将消息添加到一个新队列中,然后通过Cron作业(Cloud Watch)每5分钟处理一次该队列,并检查消息的日期并跳过新消息,仅处理5分钟或更旧的消息。 - David Gatti
1
对于SQS集成,Lambda将仅尝试重新处理与SQS队列重新驱动策略中配置的次数相同的次数。此外,不使用Lambda DQL设置。您提供的文档链接指出:“如果您正在使用Amazon SQS作为事件源,请在Amazon SQS队列本身上配置DLQ,而不是Lambda函数。” - Onema
如果您点击句子末尾的链接,您将在第一句中读到以下内容:“您可以使用AWS Lambda函数来处理标准Amazon Simple Queue Service(Amazon SQS)队列中的消息”。这意味着当您使用SQS收集由Lambda处理的消息时,而不是反过来。您被AWS糟糕的句子所欺骗 :) - David Gatti

1

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