Testing application resilience with Amazon SQS and AWS Fault Injection Service

TL;DR · AI 摘要
AWS FIS与SQS结合可系统化测试应用弹性,通过故障注入验证恢复机制有效性。
核心要点
- 使用AWS FIS模拟SQS访问中断,验证应用失败处理机制
- 通过四阶段渐进式实验暴露不同层级的故障模式
- CloudWatch指标可区分生产者与消费者端行为差异
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- 应用弹性测试
- 实验设计
- 四阶段中断测试
- IAM策略限制
- 工具使用
- AWS FIS
- SSM Automation
- 监控分析
- CloudWatch指标
- 恢复机制验证
金句 / Highlights
值得收藏与分享的关键句。
禁止使用sqs:*拒绝策略,仅限制数据平面操作可避免队列锁定
四阶段渐进式中断测试可暴露系统性故障模式
CloudWatch指标分析能区分生产者与消费者端行为差异
使用 Amazon SQS 和 AWS 故障注入服务测试应用程序的弹性 | AWS 架构博客
使用 Amazon SQS 和 AWS 故障注入服务测试应用程序的弹性
当您的应用程序无法再通过 Amazon Simple Queue Service(Amazon SQS)队列发送或接收消息时,下游处理可能会停滞。原因可能是配置错误的 Identity and Access Management(IAM)策略、网络分区、不良部署或短暂的服务事件。无论原因是什么,您的应用程序看到的都是类似的情况:SQS 操作开始失败。您的服务处理这些失败的方式(对无法成功执行的操作快速失败、打开断路器、在生产者端进行缓冲)可能在短暂中断和级联故障之间产生巨大差异。
如果您从未在故障情况下测试过这些机制,那么您依赖的是假设。通过 AWS 故障注入服务(AWS FIS),您可以首先验证这些假设。目标不是验证 SQS 是否正常工作,而是了解当 SQS 操作失败时您的应用程序会做出什么反应,以及您是否能察觉到。弹性实验可以同时测试您的恢复机制和可观测性。
在本文中,您将学习如何:
- 以明确、可衡量的假设和成功标准构建弹性实验。
- 使用 AWS FIS 和 AWS Systems Manager(SSM)自动化来模拟对 SQS 队列的渐进式访问中断。
- 解释 Amazon CloudWatch 指标以确定您的弹性机制是否正常工作,并区分生产者端和消费者端的行为。
- 识别并修复应用程序故障处理中的缺陷。
解决方案概述
在本实验中,您将使用范围限定的拒绝资源策略阻止应用程序对 SQS 的访问,然后恢复访问并观察恢复过程。您应用的策略会拒绝应用程序依赖的数据平面操作(发送、接收、删除和更改消息可见性,以及清空队列),同时不影响队列管理。中断持续时间会跨四个阶段逐步增加,以揭示不同类型的故障。如需了解更多,请参阅控制平面和数据平面。
重要:不要拒绝 sqs:*。在 IAM 策略评估中,显式的 Deny 会覆盖所有 Allow,包括自动化自身的权限,后者稍后需要移除该策略。如果拒绝策略涵盖 sqs:SetQueueAttributes、sqs:AddPermission 和 sqs:RemovePermission,可能会锁定队列,即使应用该策略的角色也无法清理它。仅将拒绝限定在数据平面操作上。有关安全策略结构,请参阅配置实验、SQS 故障排除:访问被拒绝以及这篇 re:Post 文章关于拒绝策略锁定问题。
您可以在 GitHub 上的 FIS 模板库中找到故障注入代码(SSM 自动化文档、FIS 实验模板以及两个角色的示例 IAM 策略)。
渐进式实验阶段
短暂的中断可以揭示您的故障处理机制是否激活。更长时间的中断可以暴露在持续故障下才可能出现的系统性问题。
注意:此实验测试的是您的应用程序的弹性模式,而不是 SQS 本身。范围限定的拒绝模拟了您的应用程序在网络分区、权限更改或其他访问中断期间可能经历的情况。
您可以同时观察两种不同的故障表现:
Producer 端:调用 SendMessage 的组件。当发送被拒绝时,你正在测试生产者如何处理入队失败:是快速失败、开启断路器、本地缓存还是丢弃消息?
Consumer 端:调用 ReceiveMessage 和 DeleteMessage 的组件。当接收被拒绝时,你正在测试故障期间的积压增长情况,以及恢复后重试机制的表现,消费者处理积压消息时能否跟上节奏,还是开始将消息推向死信队列。
若要隔离消费者故障(例如因部署问题或扩展问题导致消费者停止,但生产者仍在发送消息),只需拒绝 sqs:ReceiveMessage 和 sqs:DeleteMessage 操作。这可以通过编辑 SSM Automation 文档实现。
生产者服务向 SQS 队列发送消息,消费者服务从队列读取,死信队列连接到源队列,CloudWatch 收集相关指标
架构图:生产者服务 → SQS 队列 → 消费者服务,死信队列连接到源队列,CloudWatch 从生产者、消费者及两个队列收集指标。
关于示例工作负载的说明。此处的特性(断路器、本地缓存、线程池)假设生产者和消费者是长期运行的服务,而非短暂的 Lambda 调用。
定义你的假设
从一个问题开始:当 SQS 访问中断时,你能否推理出系统应该表现出的行为?这个问题本身,而非这是你的第一次实验,决定了你要撰写何种假设。
如果可以,请说明预期结果及判断依据的指标:当我们的应用失去 SQS 访问 [持续时间] 时,我们预期 [具体可观测行为]。系统在访问恢复后 [恢复预期],根据 [指标] 测量应在 [时间] 内完成。
已具备弹性模式的团队可能会这样写:当我们的订单处理应用失去 SQS 访问 5 分钟时,我们预期生产者在 30 秒内开启断路器,快速失败,并将消息缓存到本地持久化存储而非丢弃。恢复后将在 2 分钟内重放缓存消息,恢复正常处理速度,根据 NumberOfMessagesSent 回到基准值、ApproximateNumberOfMessagesVisible 在 15 分钟内降至接近零来衡量。
如果是你的首次测试,或从未演练过这种故障模式,这并非前提条件。你无需先阅读代码和配置,但对实现原理和正常负载的基本理解有助于设定测试影响的边界条件。将假设表述为探索性内容,说明你将观察到的现象而非预测结果:
我们的订单处理应用从未测试过 SQS 访问中断场景。我们将阻断访问 2 分钟,观察生产者如何处理发送失败,以及消费者是否能自主恢复,根据 NumberOfMessagesSent、ApproximateNumberOfMessagesVisible、ApproximateAgeOfOldestMessage 和应用错误率进行衡量。
无论哪种情况,在继续之前请先写下你的假设。你所写内容与实际发生之间的差距,正是系统需要改进的地方。
先决条件
GitHub 仓库包含可运行的示例。以下要点说明了应从哪个文件开始。你需要:
- 一个已配置的生产者和消费者:被测试的应用程序。这是唯一一个在代码库中没有示例的必要条件。该库提供的是故障注入功能,而非工作负载。本文中的观察表假设您的应用程序会发出断路器状态、发送失败和消息丢失计数器、回退存储写入操作以及重复处理指标。如果没有这些监控配置,您将只能看到队列指标的变化,而难以了解应用程序的实际状况。
- 一个 AWS FIS 的 IAM 角色,受 fis.amazonaws.com 信任,并具备运行 SSM 自动化文档的权限(ssm:StartAutomationExecution 及相关权限,以及 iam:PassRole)。代码库中提供了这两个部分:sqs-queue-impairment-tag-based-fis-role-iam-policy.json 用于权限配置,fis-iam-trust-relationship.json 用于信任策略。仅在启用实验日志记录时,才需要添加 Amazon CloudWatch Logs 权限。参见 AWS FIS 的日志记录部分。
- 一个用于 SSM 自动化文档的 IAM 角色,具备读取和修改目标队列策略的权限(sqs:GetQueueAttributes、sqs:SetQueueAttributes、sqs:ListQueues、sqs:ListQueueTags)。从代码库中的 sqs-queue-impairment-tag-based-ssm-automation-role-iam-policy.json 和 ssm-iam-trust-relationship.json 开始配置。示例策略中对写操作附加了 aws:ResourceTag/FIS-Ready 条件,这有助于防止自动化操作影响未标记的队列。请保留该条件。
- 标记为 FIS-Ready: True 的 SQS 队列。这限定了自动化操作的目标范围。仅对非生产环境队列进行标记,或在计划的测试窗口内使用。
- 一个 CloudWatch 仪表板和警报,将这些应用程序指标与生产者、消费者和队列的队列指标结合(参见监控策略)。代码库的 README 中包含一个可用于客户影响警报的 put-metric-alarm 命令示例,您可以将其适配为停止条件。
- 一份记录的回滚计划,以防自动化无法移除拒绝策略(如果拒绝策略导致队列管理被锁定,请参见 re:Post 关于拒绝策略锁定的文章)。
重要提示:首次运行这些实验时,请使用非生产环境。在生产环境中运行时,请确认已获得变更管理批准。
实验配置
AWS Systems Manager Automation 负责应用和移除拒绝策略;AWS FIS 负责协调执行顺序。
Systems Manager 自动化文档
SSM 自动化文档包含以下四个步骤:
- getTargetQueues:查找标记为 FIS-Ready: True 的 SQS 队列。它调用 ListQueues 接口一次,最多返回 1,000 个队列 URL。因此,在队列数量超过该限制的账户中,应在依赖该接口查找所有标记队列前添加分页或 QueueNamePrefix 过滤条件。
- applyDenyAllPolicyToQueues:向每个队列的资源策略中添加一个限定范围的拒绝语句。仅拒绝应用程序使用的数据平面操作,从不拒绝管理操作,这样自动化可以在清理时移除自己的语句。如果对自动化进行调整,请考虑添加一个验证步骤,拒绝任何覆盖管理操作的拒绝策略。此时锁定将需要同时修改策略和验证规则。
提示:可以通过在语句中添加 aws:CurrentTime 的 DateLessThan 条件,使拒绝策略自动过期,即使清理步骤从未执行,拒绝策略也会在设定时间后停止生效。参见 IAM 条件运算符中的日期和时间相关操作。
- waitForDuration:按指定的故障持续时间(ISO 8601 格式,例如 PT2M)暂停执行。
- removeDenyAllPolicyFromQueues:移除FISTemporaryDeny语句,恢复正常访问权限。该文档将onFailure和onCancel路由到此步骤,使中止的运行尝试进行清理,如果无法恢复策略,该步骤会触发错误而不是报告成功。
通过Principal选择影响范围。"Principal": "*"会将数据平面权限拒绝给所有调用者:被测应用,以及任何管理员、金丝雀发布或其他队列使用者。这忠实地模拟了服务分区,但在共享队列上会影响的不只是你的应用。若只想影响应用本身(更常见的"我的应用失去访问权限"场景),将拒绝权限限定到其IAM角色:
"Principal": { "AWS": "arn:aws:iam::<account-id>:role/<application-role>" }角色ARN会匹配该角色的所有会话,捕获应用的调用而不将拒绝权限应用于其他调用者。在共享队列上,基于Principal的拒绝权限也会改变你的度量方式:队列级指标会混合受损流量和健康流量,因此应依赖应用的客户端指标和读取恢复作为回归到事件前水平的指标,而非从零开始再恢复。将自动化流程的可选targetPrincipalArn参数设置为限定拒绝权限的目标Principal,或留空以拒绝所有Principal。本文其余部分假设使用全队列拒绝("Principal": "*")。
FIS实验模板
FIS模板将四个故障阶段与恢复期串联,通过SSM Automation文档调用时逐步增加持续时间;startAfter字段强制顺序执行。这个渐进过程就是关键所在。你可以观察到随着严重程度增加的因果关系:
阶段 | 持续时间 | 该持续时间通常暴露的问题 ---|---|--- Impair 1 | 2分钟 | 快速失败行为和熔断器激活 Recover | 3分钟 | 缓存消息重放。指标回归基线 Impair 2 | 5分钟 | 队列未排水时的积压累积 Backlog burndown | | Impair 3 | 7分钟 | 持续故障导致的线程池和内存压力 Recovery under a larger backlog. 消费者是否能跟上 Impair 4 | 15分钟 | 长时间失去访问权限下的系统性限制
FIS实验模板包含四个故障操作(2、5、7和15分钟),通过恢复等待串联
图示:FIS实验模板:四个故障操作(2、5、7和15分钟)通过恢复等待串联
停止条件。当指定的CloudWatch警报触发时,停止条件会自动终止实验,这是渐进实验的关键控制手段。触发的停止条件还会尽可能回滚:FIS取消运行,自动化流程的onCancel步骤移除拒绝权限,恢复访问。这种回滚是该实验设计的特性,而非所有停止条件的通用特性:例如EC2实例终止这样的操作不支持回滚,因此在依赖停止条件限制损害前,需检查每个操作的回滚行为。库模板默认包含"stopConditions": [{"source":"none"}],因为正确的警报取决于模板无法预设的健康信号。
指标选择至关重要:对ApproximateAgeOfOldestMessage或NumberOfMessagesSent等队列指标设置警报是错误的,因为这些指标在故障期间本应发生变化。因此,警报会在2分钟的第一阶段触发,导致运行在更长时间阶段暴露任何有趣现象前就中止。你实际上是在对注入的效应设置警报。
而不是将停止条件与一个在弹性机制正常运行时应保持健康的信号关联。这将反映真实的客户影响。如果该信号退化超过您可容忍的范围(例如,在您的假设允许的2分钟内无法恢复的错误率),则说明弹性机制已经失效,继续实验会进一步影响客户。需要考虑设置警报的指标包括:
- 应用程序错误率或事务成功率指标(例如应用程序发出的自定义CloudWatch指标,如每分钟失败订单数),这是衡量客户影响最直接的指标,且独立于您正在扰动的SQS指标。
- 负载均衡器5xx错误计数或目标响应时间(例如应用负载均衡器上的HTTPCode_Target_5XX_Count),当尚未发出业务指标时,这是一个良好的代理指标。
- 死信队列深度:死信队列的ApproximateNumberOfMessagesVisible超过阈值,表明消息永久失败而非仅临时堆积。
有关AWS FIS停止条件的更多信息,请参阅停止条件。
从假设中推导阈值。上述假设要求在访问恢复后2分钟内完成恢复。这个数字也是您的警报阈值。如果每分钟失败订单数是您的客户影响指标且基准值接近零,可将警报设置为:连续2个1分钟周期内每分钟失败订单数>10。这个时间窗口足够长,当断路器打开时短暂的峰值不会中止实验;同时足够短,如果在假设的时间窗口内未能恢复则会中止。警报的设计应基于指标在故障期间的行为,而非测试本身:当断路器打开时,低流量的自定义指标可能完全停止发送数据点。随着接近生产环境,需要收紧阈值。
当客户影响警报触发并中止实验后,AWS FIS实验处于停止状态
图:当客户影响警报触发时,AWS FIS自动中止实验(状态:已停止)
运行实验
使用AWS FIS启动实验,可通过控制台或AWS CLI:
aws fis start-experiment --experiment-template-id <YOUR_TEMPLATE_ID> --region <YOUR_REGION>故障期间需要观察的内容
在每个阶段,SQS操作会返回AccessDenied错误。注意该错误实际验证了什么和未验证什么:403错误不可重试,因此该实验验证的是代码是否能识别该错误并快速失败,而非重试路径。要验证重试和退避机制,请注入可重试的故障,如限流或超时。生产者和消费者失败方式不同,需分别观察。
SQS队列访问策略显示的FISTemporaryDeny语句(限制SendMessage和ReceiveMessage操作)
图:在故障期间,队列的访问策略包含FISTemporaryDeny语句;SendMessage和ReceiveMessage返回AccessDenied,而管理操作仍可正常执行。
生产者端(调用SendMessage的组件):
| 阶段 | 健康响应 | 不健康响应 | 需观察的信号 | |------|----------|-------------|--------------| | 第一次失败 | SendMessage识别AccessDenied并快速失败 | 崩溃、挂起或阻塞调用线程 | NumberOfMessagesSent降至~0,生产者错误率上升 | | 连续3-5次失败后 | 断路器打开,开始丢弃或缓冲负载 | 持续无限重试 | 断路器状态指标、生产者CPU/线程/连接数 | | 最终失败 | 发送失败(不可重试错误或重试预算耗尽) | | |
快速失败并将负载持久化到耐用的备用存储或发出警报,而不是静默丢弃
静默丢弃消息(永久丢失)
生产者“发送失败/丢弃”计数器。备用存储写入
应用状态
保持响应性。优雅降级
向调用者返回500错误。无限制的内存队列
生产者健康检查、请求延迟
资源使用情况
受退避和断路器限制
CPU/内存/连接数持续上升(密集重试循环)
生产者CPU、内存、连接池使用情况
注意:放弃发送的生产者不会将任何内容路由到死信队列(DLQ)。
消费者端(调用ReceiveMessage/DeleteMessage的组件):
ReceiveMessage/
DeleteMessage退避轮询循环而非持续重试。任何在途消息在可见性超时后返回队列
消费者循环崩溃或挂起
NumberOfMessagesReceivedNumberOfMessagesDeleted丢弃
积压增加(消费者无法处理)
积压告警触发。如果与队列深度关联(例如每个工作者的积压量),扩展机制会响应
积压无限制增长。消费者进入空循环
ApproximateNumberOfMessagesVisible停止处理(保持平稳或上升);
ApproximateAgeOfOldestMessage持续上升
幂等处理。可安全重试
重传时产生重复副作用
下游幂等性/重复写入指标
受可见性超时和退避限制
在途消息堆积。消费者饱和
ApproximateNumberOfMessagesNotVisible。消费者CPU/内存
故障期间不要期待死信队列(DLQ)被填充。重传由maxReceiveCount驱动:消息只有在被消费者接收指定次数且未删除后才会进入DLQ。当ReceiveMessage被拒绝时,不会有任何消息被传递,接收计数不会增加,也不会触发重传。DLQ依赖于被阻塞的调用,因此在恢复期间需要特别关注,而非故障期间。
关键CloudWatch指标及其解读:
- NumberOfMessagesSent:拒绝策略生效且生产者无法入队时会降至零
NumberOfMessagesSent在每次故障窗口期间降至零并在恢复时激增,最终在停止条件触发时停止
图:NumberOfMessagesSent在每次故障窗口期间(红色)降至零,并在恢复时(绿色)因缓冲消息重放而激增。最终阶段在停止条件触发时(橙色)结束
- ApproximateNumberOfMessagesVisible:当前可用于检索的消息积压量。故障期间通常停止变化,这种停滞正是问题的明确信号(因为没有消息被发送或处理)
- ApproximateAgeOfOldestMessage:未处理消息老化时会增加,但仅当拒绝策略生效时队列已有消息。对于空队列不会上升,因此需要与可见消息计数一起解读
ApproximateAgeOfOldestMessage在15分钟阶段可见积压未被处理时持续上升,恢复时两者同时下降
图:消费者端影响:ApproximateAgeOfOldestMessage在15分钟阶段可见积压未被处理时持续上升,恢复时两者立即同时下降
应用错误率:初始激增后,若断路器启动会趋于稳定
生产者断路器在每次故障后数秒内打开并在恢复时关闭,呈方波形态
Figure : 生产者的断路器在每次故障发生后几秒内开启(1),在恢复时关闭(0),形成一个干净的方波。由于断路器仅在经历几次持续故障后才开启,因此该波形会略微滞后于每个故障窗口。
基于计数的指标(NumberOfMessagesSent/Received/Deleted)反映系统级活动,可能包含重试和重复消息,因此应将其视为趋势指标而非精确的唯一消息计数。
恢复期间需要观察的内容
当拒绝策略被移除时,生产者和消费者会按照不同的时间线进行恢复。
生产者侧:
需要观察的现象
- 发送恢复:发送速率回升至基准水平。断路器进入半开状态,随后在约30秒内关闭
- 断路器保持开启状态(过时的故障状态):需要手动重启
- 缓冲/备用负载:
- 从持久化备用存储中重放并以幂等方式重新发送
- 永久丢失(如果在故障期间被静默丢弃)
故障期间的缓冲机制: 生产者将消息缓冲到持久化备用存储,并在恢复时重放缓冲内容
Figure : 故障期间生产者将消息缓冲到持久化备用存储(无消息丢失),恢复时重放缓冲内容:发送成功、缓冲写入和重放过程在整个运行周期中的表现。
消费者侧:
- 接收/删除恢复:
- 消费者保持卡顿状态。无自动恢复
- 消费速率持续下降;
- 消费停滞:速率突然飙升后降至零并保持(消费者过载或卡死)
- 死信队列(DLQ)内容:
- 真正有害的消息在DLQ保留期内以受控批次重新驱动和重新处理
- 所有消息一次性重新处理(导致下游过载),或留在DLQ中直至过期被删除
恢复期间DLQ才能移动。被积压消息淹没的消费者可能重新失败并推送部分消息到DLQ。如果健康消息出现在DLQ中,说明您的maxReceiveCount设置过低或消费者处理速度不足。
结果分析
实验完成后,将实际发生的情况与假设进行对比,重点关注以下问题:
- 断路器是否激活?从首次AccessDenied到应用程序停止尝试SQS操作的时间间隔进行测量。若超过目标值(通常为30秒),说明检测阈值设置过高。
- 系统是否保留了消息?将发送尝试次数与恢复后处理的消息数、DLQ和生产者侧备用存储中的消息数进行核对。若数据不匹配,说明存在消息丢失,差距可指示哪一侧丢失了消息。
- 恢复的消息是否仍然值得处理?保留性和相关性是两个不同问题。长时间停机后,部分缓冲发送和积压消息可能代表客户端已放弃的请求,处理这些消息会浪费恢复资源去执行过时的意图。在消费时将每条消息的时间戳与当前时间对比,主动丢弃或隔离所有不再可操作的消息。参见REL05-BP04:快速失败并限制队列。
- 恢复行为如何?查看每次恢复周期后的ApproximateNumberOfMessagesVisible。健康系统应持续稳定地清空消息。若出现清空停滞(速率突然飙升后降至零并保持),说明消费者过载或卡死。
- 更长的中断是否揭示了新的故障模式?将 2 分钟阶段与 15 分钟阶段进行对比。只有在持续故障情况下才会显现的问题:累积重试线程导致线程池耗尽、缓冲消息带来的内存压力、连接池资源枯竭、死信队列(DLQ)消息因超过保留期限被删除(详见最佳实践)。
与你的假设相符的结果证明你的弹性机制有效。与假设不符的结果则需要你重点关注。
最佳实践
以下章节将介绍如何将本实验暴露的漏洞转化为解决方案的弹性模式:重试逻辑、断路器、死信队列和监控。
带有指数退避的重试逻辑
不要对所有错误都进行重试。仅对可能通过重复尝试成功解决的错误(如限流、超时和短暂的 5xx 错误)进行重试,而对不可重试的错误(如本实验注入的 AccessDenied(403))应快速失败。对于值得重试的错误,应使用带抖动的指数退避策略:每次失败后等待时间呈指数级增长(1 秒、2 秒、4 秒、8 秒等),并加入随机偏移量,避免同时失败的生产者同时重试,从而防止对正在恢复的依赖项造成流量冲击。
AWS SDK 已内置可配置的重试行为,因此应使用其内置功能而非自行实现。详见 超时、重试与带抖动的退避。
断路器
断路器在连续失败次数达到阈值后会停止尝试操作,待恢复超时后允许一次测试调用通过。这可以节省资源避免重复失败调用,同时为依赖项提供恢复空间。需谨慎选择断路器开启状态下的行为:相比静默切换到备用路径,主动丢弃或缓冲负载更安全,因为备用路径通常仅在故障期间被使用,且往往与主路径同时失效。详见 使用负载丢弃避免过载 和 分布式系统中避免备用路径。
死信队列
为每个队列配置死信队列(DLQ)。DLQ 是消费端处理毒消息的安全网,而非生产端的溢出缓冲区。将 maxReceiveCount 设置为适合你工作负载的处理尝试次数(通常为 3 到 5 次)。由于重试是向 DLQ 写入消息的唯一方式,每个消费者都必须容忍看到消息两次。详见 通过幂等 API 使重试更安全。关于恢复后大量积压消息可能造成的影响,详见 避免无法逾越的队列积压。
DLQ 没有深度限制,唯一约束是消息保留期。超过保留期后 SQS 会删除消息(默认 4 天,最长 14 天)。为延长消息删除前的调查时间,应将 DLQ 的保留期设置得比源队列更长。对于标准队列需注意:保留期计时器从最初入队时间开始计算,转移到 DLQ 后不会重置,因此源队列中的时间会消耗保留期(FIFO 队列会重置计时器)。实验结束后应确认消息被保留而非因超期被删除。
监控策略
关于监控指标的粒度和内容,详见 为运维可视性构建分布式系统监控。构建一个 CloudWatch 仪表板,将以下指标从生产者、消费者和队列三个维度进行聚合:
队列级指标:NumberOfMessagesSent(发送消息数)、NumberOfMessagesReceived(接收消息数)、NumberOfMessagesDeleted(删除消息数)、ApproximateNumberOfMessagesVisible(可见消息数估算值)、ApproximateNumberOfMessagesNotVisible(不可见消息数估算值)、ApproximateAgeOfOldestMessage(最旧消息年龄估算值),具体观测方式详见 故障期间应观测的内容。
应用层指标:
- 按类型划分的错误率(区分AccessDenied与其他失败情况),并按生产者与消费者进行标记。
- 断路器状态变化(打开/关闭/半开状态转换)。
- 死信队列消息数量。
- 端到端消息处理延迟。
当ApproximateAgeOfOldestMessage超过SLA阈值时触发告警作为生产环境告警,但不作为实验停止条件,因为该指标在系统受损期间应会上升。此处应使用客户影响信号(参见停止条件)。
清理环境
- 验证拒绝策略已移除。在控制台检查每个队列的访问策略,或运行
aws sqs get-queue-attributes --queue-url <URL> --attribute-names Policy。如果FISTemporaryDeny仍然存在,请获取策略,删除语句,并通过aws sqs set-queue-attributes重新应用。
- 处理实验期间进入死信队列的消息。
- 查看CloudWatch指标,确认队列已恢复正常运行。
- 记录发现结果:哪些与假设相符,哪些不符,以及需要修复的内容。
扩展弹性测试
在基础测试通过后,扩展实验范围:
部分故障:仅使部分队列失效,测试应用是否能处理混合的健康/不健康依赖项。
注意:不要对同一队列同时运行两个故障实验。自动化流程会读取策略、修改并写回策略。并发运行可能导致相互覆盖并留下过期的拒绝策略。请针对不同的队列运行,或按顺序执行。
仅消费者端:仅阻止ReceiveMessage和DeleteMessage操作,同时允许SendMessage,以模拟消费者故障而生产者继续向队列写入消息(最常见的现实场景)。
与其他故障结合:将SQS实验与EC2实例终止或网络延迟注入结合运行,测试复合故障场景。
探索AWS Resilience Hub:使用AWS Resilience Hub评估应用的弹性状态并获取改进建议。
使用FIS场景
场景是AWS提供的模板,包含可识别事件的动作、目标和持续时间,因此您可以从经过审核的定义开始,而无需自行组装动作。虽然AWS库中提供了多个场景,但以下两个是不错的起点。
可用性区域(AZ)可用性:电源中断模拟一个可用性区域失去电力的症状:该区域的EC2、ECS和EKS计算实例停止,该区域的新启动失败,子网连接丢失。这是对基于队列的解耦更严格的测试,因为生产者和消费者失去容量时队列本身并未成为目标。您可以验证剩余消费者是否能吸收积压消息,Auto Scaling是否在剩余可用性区域替换容量而非在受损区域重试,以及积压消息是否在假设的时间窗口内被清除。默认设置为30分钟故障加30分钟恢复,是本文最长阶段的两倍。
AZ:应用性能下降会在同一可用区(AZ)内的资源之间引入额外的延迟。这种延迟会导致应用性能下降的许多症状,有时被称为部分中断或灰度故障。它会增加目标资源之间网络流的延迟。网络流表示计算资源之间的流量:服务器、容器和服务之间的数据包,承载着请求、响应和其他通信。该场景可用于验证可观测性设置、调整告警阈值、发现应用对性能下降的敏感性,并演练关键运维决策,如AZ疏散。
场景具有相同的义务:首先撰写假设,并在客户影响指标上设置停止条件,而不是场景设计所要推动的指标。您推导出的阈值保持不变。将场景复制到您自己的模板中,可以缩小目标范围或更改持续时间。请参阅《AWS FIS场景库使用指南》。
结论
在本文中,您学习了当SQS操作失败时,应用会执行什么操作,以及您是否能够察觉到这些情况。假设与结果之间的每一个差距都是提高系统弹性和可观测性的机会。
从非生产环境中的2分钟阶段开始。修复出现故障的部分。然后运行完整流程,并随着应用的演进持续运行。每个增长阶段都会带来仅在负载下才能发现的故障模式。
更多信息请参阅:
- AWS FIS实验模板
- Amazon SQS资源策略
- Amazon SQS死信队列
- Amazon SQS可用的CloudWatch指标
- 使用混沌工程验证工作负载的弹性
- AWS Well-Architected可靠性支柱:使用混沌工程测试弹性
- AWS Well-Architected可靠性支柱:REL05-BP04快速失败并限制队列
- GitHub上的AWS FIS示例
作者简介
'"`