为无状态队列使用者创建无服务器自定义重试机制

作者: 凯扎德·瓦迪亚 |

像Amazon Lambda这样的无服务器队列处理器通常存在于架构中,它们从Amazon Simple Queue Service(亚马逊 SQS)等队列中提取消息,并在分布式架构中与下游服务或外部API进行交互。由于这些下游服务容易受到短期中断或节流的影响,因此强大的重试方法对于提供可靠的消息处理是必要的。这通常需要实现具有死信队列 (DLQ) 和指数退避等功能的特殊重试逻辑,以优雅地处理这些情况,确保下游系统不会被过多的重试所淹没。

在这篇文章中,我们提出了一种解决方案,该解决方案可以在工作流程的状态不受其他服务管理的情况下处理无服务器重试。

解决方案概述

当 Lambda 函数在使用来自 SQS 队列的消息后与下游服务进行交互时,需要一些自定义重试逻辑。该策略涉及在 Lambda 中使用亚马逊事件桥计划程序和代码。核心概念是实现强大的重试机制,使用 EventBridge 调度器处理失败的消息处理尝试。当 Lambda 函数在处理消息时遇到问题时,它会触发特定错误。在 catch 区块中发现此错误后,该函数会生成 EventBridge 时间表。因此,该消息被发送回 SQS 队列,并将在未来的指定时间再次可供处理。

在这种方法中,重试机制可以对重试时间进行细致的控制,这也可能支持各种技术,包括指数退避和线性重试间隔。这种方法将重试逻辑与用于处理消息本身的代码分开,从而使 Lambda 函数具有良好的性能。除了在所有重试都用尽时处理消息外,该解决方案还与 DLQ 接口,将此类消息与主队列分开。

下图说明了解决方案架构。

Lambda 函数代码中的错误处理和重试选择逻辑构成了如何实现此自定义重试机制的基础。如果在处理消息时出现错误,则该函数会引发特定的异常。然后引发异常会启动重试流程。try-catch 区块会捕获此异常,并调用与 EventBridge Scheduler API 接口连接的函数来构建自定义日程安排。为了配置时间表,我们包括目标 SQS 队列和重试消息时的预期时间戳。我们可以根据多个参数(例如错误类型、先前重试次数或其他自定义退避方案)对代码进行一些修改来更改延迟。

作为该方法的一部分,我们使用 SQS 消息属性来实现熵等性并跟踪重试次数。每次重试时,该函数都会将新的时间戳添加到消息正文中的数组中。如果该函数消耗消息的次数超过最大重试限制(由重试次数组确定),它会在不重新安排时间的情况下将消息发送到 DLQ。

该解决方案还涉及集成 DLQ,这样它就不会将消息保留在主处理队列中,也不会永久重试。如果超过最大重试限制或某些错误场景要求其提前停止,Lambda 函数将向 DLQ 注册消息。该队列会保留所有失败的通信,直到可以手动查看、重新处理甚至更正为止。

注意事项和优秀实践

在将这种自定义重试系统付诸实践时,需要记住一些关键因素。一个方面是处理部分故障,即在仅完成部分步骤的情况下进行处理。在这种情况下,我们可以使用某种形式的补偿操作或回滚来保持数据的一致性并避免队列使用者下游出现差异。

另一个关键因素是控制重试限制。尽管系统设计允许变化的重试限制,但我们必须平衡资源使用和弹性。重试次数过多可能会导致更高的成本,并导致速度变慢或服务降级。这就是为什么我们建议在考虑可能的失败率、SLA 和失败的业务后果的基础上设置适当的重试限制。

我们还必须考虑到,EventBridge Scheduler 的粒度为 1 分钟,而且队列和函数之间还有额外的延迟,因此该机制不会完全精确。原则上,调度器设置了处理消息的最短时间,从而确保 Lambda 函数至少遵守速率限制。这也可能导致更多的延迟,因此需要针对时间敏感的申请对机制进行调整,以考虑到这些延迟。

由于该解决方案可能会处理不同数量的消息和处理负载,因此扩展问题也很重要。例如,队列的 Lambda 并发和保留期代表我们应监控和调整的资源配置,以实现优秀性能和成本。

最后,我们需要将安全视为解决方案的一部分。如果下游服务在虚拟私有云 (VPC) 中运行,我们还需要将 Lambda 函数放在 VPC 中。在这种情况下,我们需要通过 Amazon PrivateLink 访问 EventBridge Scheduler,这样可以在 VPC 内安全高效地访问服务。

此外,使用最低权限的委托人实施亚马逊云科技身份和访问管理 (IAM) 角色(主要是 Lambda 函数角色)非常重要,这使它能够创建事件总桥日程安排(并iam:PassRole向计划者提供所需的权限),以及将调度程序的 IAM 角色传递给它。调度程序的角色只需要权限即可将消息放入源队列。我们还需要授予函数访问权限,以便在 DLQ 中放置消息并从源队列接收消息。

监控和故障排除

自定义重试机制需要高效的监控和调试。考虑到这一点,我们可以使用亚马逊 CloudWatch 日志和指标查看系统的各种行为并识别潜在的问题。

Lambda 函数的调用次数、相关的错误率、运行时和 DLQ 的使用是我们应该监控的关键指标。值得在 CloudWatch 中设置警报,以便在 Lambda 函数的指标超过某些预定阈值时发送警报或启动自动操作。通过这样做,我们可以主动发现并解决与该职能相关的某些问题。

此外,我们可以检查 Lambda 函数的日志,以了解某些错误情况、重试模式、下游服务或重试逻辑本身的问题。我们可以在函数代码中谨慎地放置日志行,以记录相关信息,包括消息属性、重试尝试和错误细节。

未来的增强

我们可以考虑进行一些改进,以进一步增强建议方法的功能和灵活性,这为自定义重试机制提供了基础。

一种可能的改进是根据下游服务的条件或错误种类引入动态重试间隔。系统可能会根据检测到的特定错误类型或实时运行状况监控来动态调整重试间隔,而不是基于预定义的退避方案。这个概念的主要缺点是额外的复杂性,这可能会导致重试过程本身的失败。

另一项潜在的增强功能是将系统与外部配置服务(例如亚马逊动态数据库或参数存储)集成,这是Amazon Systems Manager的一项功能。这样,我们可以集中动态地处理重试配置,从而便于维护和修改重试策略,而无需重新部署 Lambda 函数代码。

还可以在该系统中内置高级错误分析和报告功能。然后,该系统将有可能通过全面的报告、分析的错误模式以及与下游服务运行状况相关的故障,为根本原因分析和主动补救提供关键见解。

结论

构建可能需要与外部服务进行通信的可扩展、强大的无服务器应用程序通常具有挑战性。但是,使用Lambda、Amazon SQS和EventBridge计划程序的拟议解决方案为实现自定义重试机制提供了一种简单而有效的解决方案。它为开发人员提供了对重试间隔的精细控制,支持指数退避等场景,并可与针对持续失败的 DLQ 和用于延迟重试消息的 EventBridge Scheduler 无缝协作。该机制还可以更广泛地重复用于无状态队列使用者,而不仅仅是 Lambda 函数。这种模式使开发人员能够实现强大、容错的无服务器系统,以优雅地处理下游服务的中断。


作者简介



凯扎德·瓦迪亚

凯扎德·瓦迪亚

Kaizad Wadia 是亚马逊网络服务的解决方案架构师,帮助客户在亚马逊云科技上部署强大的应用程序并最大限度地发挥其数据潜力,通过量身定制的云解决方案推动创新和业务增长。


*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您发展海外业务和/或了解行业前沿技术选择推荐该服务。