关于 resilience-failure
用于设计远程依赖和共享资源韧性的技能,帮助系统设计者根据可用性、延迟和重试安全性选择故障防护措施,并评估取舍与压力场景。
主要功能
- 01超时与重试策略
- 02熔断器与舱壁隔离
- 03优雅降级与故障切换
- 04速率限制与负载削减
- 05压力场景与监控
适用场景
- 01制定依赖变慢或不可用时的行为
- 02防止过载与重试风暴
- 03规划恢复流程,避免服务承压过大
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill resilience-failure安装后在 Claude Code 中输入/resilience-failure确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/deep-dive.mdreferences/providers/aws.mdazure.mdgcp.mdgeneric.mdtemporal.md详细介绍
韧性与故障处理
设计系统时,要确保某个部分发生故障——这不可避免——时,故障能被控制在局部,用户仍能获得有用的(即使有所降级的)答复,而不是看到错误页面或遭遇连锁故障。这里做得好不好,决定了你面对的是依赖项变慢,还是系统彻底崩溃:造成故障扩大的最常见因素,恰恰是系统自身对此的反应(重试风暴、健康检查请求骤增)。
适用场景
任何依赖远程服务、共享资源或有 SLA 的设计。可在此查找单点故障,确定依赖项变慢或宕机时每次调用该如何处理,保护服务免受过载(限流),并规划服务恢复后如何恢复运行而不被积压请求压垮。
不适用场景
不要为单个进程内函数或尽力而为的批处理任务套上熔断器和舱壁隔离——这些机制是为跨进程/跨网络调用准备的(YAGNI)。不要在未设置幂等键前,为非幂等写入添加重试(→ api-design),否则会重复产生副作用。满足可用性目标的最低成本设计就是最佳设计;追求并不需要的额外一个 9 会带来实际的复杂性(→ back-of-the-envelope,了解额外一个 9 实际能带来什么)。
先明确
- 可用性目标——需要几个 9?按请求衡量,还是按功能衡量?(→
back-of-the-envelope。) - 故障影响范围——如果这个依赖项宕机,整个请求都必须失败,还是可以让功能降级或隐藏?
- 幂等性——操作是否可以安全重试?如果不能,如何确保安全(键、去重)?(→
api-design。) - 延迟预算——等待多久后,超时会比继续等待更好?(→
back-of-the-envelope。) - 限流维度与策略——按用户/IP/API 密钥/租户限流?采用硬限流(拒绝)还是软限流(排队/调整流量)?可容忍多大的突发流量?
可选方案
采用分层防护;大多数实际设计都会组合使用多种方案。
- 超时——为每次远程调用设置上限。应在所有地方使用;无上限等待是大多数连锁故障的根源。
- 带退避和抖动的重试——以逐渐增加且随机化的间隔,重新尝试临时性故障。适用于针对短暂故障的幂等调用;绝不要裸重试。
- 熔断器——停止调用发生故障的依赖项;快速失败,并通过探测尝试恢复。适用于下游宕机或变慢、重试会不断堆积的情况。
- 舱壁隔离——按依赖项隔离资源(线程池、连接池、队列)。这样一个变慢的依赖项就不会耗尽其他依赖项共享的容量。
- 优雅降级——回退到缓存/过期值、部分结果、默认值,或隐藏功能。适用于可用但效果较差的答复优于错误的情况。
- 限流/负载丢弃——限制进入系统的工作量;拒绝超额请求或调整其流量。适用于保护服务免受过载、滥用或请求方突发流量的情况。
- 冗余/故障转移——每个组件都运行 N>1 个实例;发生故障时提升备用实例。用于消除单点故障。(健康检查/负载均衡器故障转移详见
load-balancing。)
限流算法(令牌桶、漏桶、固定窗口/滑动窗口)和熔断器状态机的详细说明见 references/deep-dive.md。
权衡取舍
| 选项 | 解决的问题 | 带来的问题 | 何时调整 |
|---|---|---|---|
| 超时 | 限制线程阻塞;防止一次缓慢调用一直卡住调用方 | 设得太短 → 误报失败;设得太长 → 引发级联 | 根据依赖项的 p99 调整,而不是凭感觉 |
| 重试 + 退避 + 抖动 | 应对短暂故障 | 增加负载;重复执行非幂等写入 | 加入抖动、限制尝试次数并设置预算;要求操作具备幂等性 |
| 熔断器 | 快速失败,为故障依赖项留出恢复空间 | 增加状态和调优工作;可能因短暂故障而触发,导致过度拒绝请求 | 状态反复抖动 → 调整阈值 / 半开探测频率 |
| 隔舱 | 将单个故障限制在其专属线程池内 | 峰值利用率降低;需要配置更多线程池 | 某个高噪声依赖项耗尽其他依赖项的资源 |
| 优雅降级 | 依赖项故障时仍能继续为用户提供服务 | 提供过时或不完整的内容;需要测试更多代码路径 | 必须保证完全正确 → 改为失败时关闭 |
| 限流 | 保护服务;限制成本和滥用 | 拒绝合法的突发请求;规模扩大时需要共享状态 | 限制过严(丢弃有效请求)或过松(导致过载) |
| 冗余 / 故障转移 | 消除单点故障;应对节点 / 区域故障 | 成本、复制延迟、故障转移的一致性风险 | 故障转移导致未复制的写入丢失 → consistency-coordination |
压力下的行为
此部分旨在防止系统放大自身的故障。
- 重试风暴:依赖项变慢,每个调用方都开始重试,上游调用方的重试请求又层层叠加,负载呈几何级数增长。*缓解措施:*使用带抖动的指数退避、为每个请求设置重试预算(限制总尝试次数),并使用熔断器,避免不断重试已故障的依赖项。
- **恢复时的惊群:**服务恢复后,所有排队的客户端和过期的缓存条目同时访问该服务,再次将其压垮。*缓解措施:*使用仅逐步放行少量请求的半开熔断器、带抖动的客户端重连、请求合并,以及缓慢增加流量。(缓存过期导致的惊群见
caching。) - **健康检查惊群 / 意外 DDoS:**过于频繁的健康检查或负载均衡器探测会猛烈请求正在恢复的实例。*缓解措施:*采用温和的探测间隔、快速失败的就绪检查和连接排空。(探测机制见
load-balancing。) - **无超时导致的级联:**缓慢的依赖项一直占用线程,直到线程池耗尽,调用方在其调用方看来也变成了“不可用”。*缓解措施:*在所有位置设置超时和隔舱。
- **限流器成为单点故障:**用于计数的共享存储(例如 Redis)发生故障。*缓解措施:*为保证可用性,选择故障时放行(限流器出错时允许请求);或为加强保护,选择故障时拒绝请求——务必审慎决定。
**监控:**每个依赖项的错误率和 p99、重试次数、熔断器状态转换、线程池饱和度 / 队列深度、限流拒绝率,以及恢复后“首次成功耗时”。
如何应用
- 明确输入 — 确定可用性目标、每个依赖的故障影响范围、幂等性、延迟预算,以及限流维度/策略(“先明确”清单)。这些问题都有答案之前,不要选择任何防护措施。
- 选择防护措施 — 针对每个依赖逐一查看权衡表,而不是一概而论。每次远程调用都要设置 超时;仅对幂等操作添加重试和抖动;在下游故障会导致请求堆积时添加 熔断器;使用 舱壁 隔离共享池;根据是否可接受过时响应,选择 降级 还是 故障时关闭。
- 设置关键参数 — 超时 = 依赖的实测 p99;重试上限(通常为 2–3 次),再加上每请求预算和抖动;熔断器的开启/半开启阈值;舱壁池大小;限流器的速率/突发值,以及故障时开放还是关闭。
- 对设计进行压力测试 — 重现“压力下的行为”中的每种放大效应(重试风暴、恢复请求群、健康检查踩踏、无超时级联、限流器成为单点故障),并确认每种情况都有对应的缓解措施。
- 用数字估算容量 — 计算请求路径上的组合可用性(串联时相乘,并联时增加 9),并确认能达到目标且不过度配置。(→
back-of-the-envelope。) - 选择提供商 — 默认采用通用方案;只有用户指定云平台时,才阅读对应的提供商文件(见“选择提供商”)。
- 在回答中展示权衡 — 提供一张
决策 | 解决的问题 | 带来的风险 | 在……情况下调整表格,为此处做出的每项选择各列一行(例如,所选方案、存储方式及故障策略);最后一列写出具体触发条件(数值或可观察信号)。然后说明最先会出问题的环节。
应做与不应做
应做
- 为每次远程调用设置超时,并根据依赖的 p99 调整。
- 添加抖动和重试预算,避免重试不断累积成风暴。
- 明确标示降级响应(
stale: true),不要悄无声息地返回不实信息。 - 审慎决定限流器和熔断器在故障时是开放还是关闭。
- 在宣称设计具有韧性之前,针对各种放大效应进行压力测试。
不应做
- 不带幂等键就重试非幂等写入(→
api-design)。 - 为进程内调用套上熔断器/舱壁——这些机制是为跨网络调用设计的。
- 追求 SLA 并不要求的额外 9;冗余成本并非线性增长。
- 让共享限流器或计数器存储成为不设防的单点故障。
- 用激进的健康检查或完整重连来冲击正在恢复的实例。
重要数值
超时应根据依赖的实测 p99 确定,而不是随便估一个数。限制重试次数(通常为 2–3 次),并设置预算,避免总尝试次数失控。每多一个“9”,所需的冗余成本就会不成比例地增加——设定目标之前,先了解一个 9 究竟能带来什么。组合可用性很重要:串联组件的可用性相乘(请求路径中两个可用性为 99.9% 的依赖,组合后约为 99.8%);并联冗余组件则能增加 9。有关延迟表、9 的表格以及串联/并联可用性计算,请参阅 back-of-the-envelope。
接口示意
这里有两个不可或缺的契约。
- 降级响应: 明确标示“我处于降级状态”,不要悄无声息地返回不实信息。返回回退结果并附带信号,例如
{ "data": [...], "stale": true, "source": "cache", "as_of": "2026-05-29T10:00Z" },以便调用方和客户端做出相应处理。 - 限流响应: 使用 HTTP
429 Too Many Requests拒绝请求,并返回标准标头——X-RateLimit-Limit、X-RateLimit-Remaining和Retry-After(秒),这样行为规范的客户端就会退避,而不是继续重试直至碰壁。
选择提供商
默认采用上文的通用方案(韧性库、令牌桶/漏桶限流器、健康检查、N+1 冗余)。如果用户指定了云平台,请阅读 references/providers/<provider>.md,了解托管服务映射、配额/限制和提供商特定的权衡。如果该提供商没有对应文件,则采用通用方案。
图示
要直观展示回退路径(网关 → 主路径超时 → 虚线箭头指向缓存/默认值)或熔断器状态机,请使用插件内的 architecture-diagram 技能;将降级路径画成虚线箭头,并用错误颜色标示故障依赖项。
相关构建模块
messaging-streaming—— 与之配合:队列吸收写入流量峰值,死信队列收纳毒消息;投递保障和 DLQ 机制这一 自有概念归属其中。load-balancing—— 依赖它进行健康检查和负载均衡级别的故障转移路由;将其探测与此处的冗余机制配合,以消除单点故障。consistency-coordination—— 自有概念归属其中:故障转移带来的一致性后果(未复制的写入丢失、分区时的仲裁)由此处决定。api-design—— 安全重试写入前,依赖其幂等键契约。caching—— 与优雅降级配合,作为回退来源;缓存过期引发的惊群(而非此处的恢复惊群)这一 自有概念归属其中。system-design—— 为编排器提供输入;此模块是其中第 5 步的故障模式检查。
参考资料
references/deep-dive.md—— 熔断器状态机、退避/抖动公式、重试预算、五种限流算法(令牌桶、漏桶、固定窗口/滑动窗口)及其分布式计数器和竞态条件处理、隔离舱容量规划、单点故障分析和故障转移模式。详细设计弹性层时请阅读。references/providers/{generic,aws,azure,gcp,temporal}.md—— 各环境的服务映射、限制和注意事项;temporal.md介绍了作为工作流原语的持久化重试/超时和 Saga 补偿。