name: 韧性模式 description: 用于实现断路器、重试、舱壁或其他韧性模式。涵盖分布式系统的故障处理策略。 allowed-tools: 读取, 全局搜索, 文本搜索
韧性模式
用于构建能够优雅处理故障、优雅降级和自动恢复的系统的模式。
何时使用此技能
- 实现断路器
- 设计重试策略
- 使用舱壁隔离故障
- 构建容错系统
- 处理级联故障
为什么韧性重要
在分布式系统中,故障不是例外——而是常态。
网络会失败。服务会崩溃。数据库会超时。
问题不是如果,而是何时。
韧性 = 能够优雅地处理故障
目标:
- 防止级联故障
- 优雅降级
- 自动恢复
- 保持可用性
核心韧性模式
1. 重试模式
什么:自动重试失败的操作
何时:瞬态故障(网络波动、暂时不可用)
简单重试:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 请求 │────►│ 失败 │────►│ 重试 │───► 成功
└─────────┘ └─────────┘ └─────────┘
使用退避:
请求 → 失败 → 等待 100ms → 重试
失败 → 等待 200ms → 重试
失败 → 等待 400ms → 重试
失败 → 放弃
退避策略:
- 固定:每次重试等待相同时间
- 线性:100ms, 200ms, 300ms...
- 指数:100ms, 200ms, 400ms, 800ms...
- 指数 + 抖动:添加随机性以防止惊群效应
重试最佳实践
做:
- 添加抖动以防止惊群效应
- 设置最大重试次数
- 使用指数退避
- 仅重试瞬态故障
- 记录重试以提高可见性
不做:
- 盲目重试非幂等操作
- 重试客户端错误(400s)
- 无限重试
- 对所有重试使用相同延迟
2. 断路器模式
什么:暂时停止调用失败的服务
何时:服务持续失败
状态:
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌────────┐ 失败 ┌────────┐ 超时 │
│ │ 关闭 │─────────────►│ 打开 │─────────────┐ │
│ │ │ │ │ │ │
│ └────┬───┘ └────────┘ │ │
│ │ ▲ │ │
│ │ │ ▼ │
│ │ 成功 失败 ┌────────┐ │
│ └────────────────────────────────────►│ 半开 │ │
│ 成功 │ │ │
│ ◄───────────────┴────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
关闭:正常操作,请求通过
打开:失败超过阈值,快速失败
半开:测试服务是否恢复
断路器配置
关键参数:
失败阈值:多少失败后打开
- 太低:轻微问题就打开
- 太高:保护不足
- 典型:5-10次失败或50%错误率
超时(打开持续时间):保持打开多久
- 太短:可能太快重试
- 太长:恢复慢
- 典型:30-60秒
成功阈值:从半开关闭所需的成功次数
- 典型:1-3次成功请求
3. 舱壁模式
什么:隔离组件以包含故障
何时:防止一个故障拖垮整个系统
船类比:
┌─────────────────────────────────────────────┐
│ 无舱壁的船 │
│ ┌───────────────────────────────────────┐ │
│ │ 一个洞 → 整艘船进水 │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 有舱壁的船 │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ │ │ X │ │ │ │ │ │
│ │ 正常 │ │进水│ │ 正常 │ │ 正常 │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
│ 一个舱室进水,其他保持干燥 │
└─────────────────────────────────────────────┘
舱壁实现
线程池隔离:
┌────────────────────────────────────────────────────────┐
│ 应用程序 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 服务A 池 │ │ 服务B 池 │ │
│ │ [10 线程] │ │ [10 线程] │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ 服务 A 服务 B │
│ (慢) (健康) │
└────────────────────────────────────────────────────────┘
服务A慢不会耗尽服务B的线程
信号量隔离:
- 限制每个依赖的并发请求
- 比线程池更轻量
- 适合异步操作
4. 超时模式
什么:限制操作等待时间
何时:始终(每个外部调用都需要超时)
无超时:
请求 → 服务挂起 → 调用者永远等待 → 资源耗尽
有超时:
请求 → 服务挂起 → 5秒后超时 → 调用者处理失败
超时类型:
- 连接超时:建立连接的时间
- 读取超时:接收响应的时间
- 总体超时:允许的总时间
超时最佳实践
设置超时:
- 连接:1-5秒(快速失败)
- 读取:基于p99延迟 + 缓冲
- 总体:连接 + 读取 + 处理之和
例子:
连接超时:2s
读取超时:10s(p99是5s,2倍缓冲)
总体超时:15s
级联考虑:
如果A调用B调用C:
- C超时 < B超时 < A超时
- 每层有重试缓冲
5. 回退模式
什么:当主要失败时提供替代方案
何时:有降级但可接受的替代方案
回退选项:
┌────────────────────────────────────────────────────────┐
│ 主要失败?选项: │
│ │
│ 1. 缓存数据:返回陈旧但有效的数据 │
│ 2. 默认值:返回安全默认值 │
│ 3. 降级服务:减少功能性 │
│ 4. 替代服务:不同提供者 │
│ 5. 优雅错误:友好错误信息 │
└────────────────────────────────────────────────────────┘
例子:
主要:实时价格服务
回退1:缓存价格(< 5分钟旧)
回退2:最后已知价格带警告
回退3:"价格暂时不可用"
6. 速率限制模式
什么:控制请求速率
何时:保护服务免受过载
客户端速率限制:
- 限制发出请求
- 防止压垮依赖
服务器端速率限制:
- 限制进入请求
- 防止流量峰值
参见:rate-limiting-patterns技能获取详情
模式组合
典型韧性堆栈
请求流:
┌─────────────────────────────────────────────────────────┐
│ │
│ ┌────────────┐ │
│ │ 超时 │ ← 总体请求超时 │
│ │ ┌────────┐ │ │
│ │ │ 重试 │ │ ← 使用指数退避 │
│ │ │┌──────┐│ │ │
│ │ ││断路器││ ← 如果服务关闭则快速失败 │
│ │ ││ │ │ │
│ │ │└──────┘│ │ │
│ │ │┌──────┐│ │ │
│ │ ││舱壁│ ← 与其他调用隔离 │
│ │ │└──────┘│ │ │
│ │ └────────┘ │ │
│ └────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 服务 │ │
│ └──────────┘ │
│ │ │
│ 失败?──────► 回退 │
│ │
└─────────────────────────────────────────────────────────┘
应用顺序
从外到内:
1. 超时:总体时间限制
2. 重试:从瞬态故障尝试恢复
3. 断路器:停止调用失败服务
4. 舱壁:将此调用与其他隔离
5. [调用服务]
6. 回退:优雅处理失败
负载脱落
什么是负载脱落?
当系统过载时:
- 接受能处理的
- 优雅拒绝其余
- 更好地为一些用户服务好,而不是所有用户差
基于优先级脱落:
- 高优先级:从不脱落
- 中:中等负载时脱落
- 低:首先脱落
实现
方法:
1. 基于队列:
- 固定大小队列
- 队列满时拒绝
- 基于优先级服务
2. 基于速率:
- 每秒最大请求数
- 超时时拒绝
- 返回503或429
3. 自适应:
- 监控延迟/错误率
- 随着压力增加减少接受
- 随着系统稳定恢复
优雅降级
降级级别
级别0:完全功能性
└── 一切正常
级别1:禁用非必要功能
└── 推荐关闭,分析延迟
级别2:减少功能性
└── 只读模式,仅缓存数据
级别3:最小功能性
└── 仅核心功能,无个性化
级别4:维护模式
└── 静态页面,"很快回来"
过渡:
基于系统健康指标自动
或通过功能标志手动
功能降级例子
高负载时的电子商务:
完整功能:
- 实时库存
- 个性化推荐
- 实时聊天支持
- 详细分析
降级:
- 缓存库存(5分钟延迟)
- 通用推荐
- 仅联系表单
- 分析排队
最小:
- 静态"有货"状态
- 无推荐
- 仅邮件支持
- 分析丢弃
健康检查
健康检查类型
1. 存活检查:
"进程是否存活?"
- 简单ping
- 如果运行返回200
- 用于重启决策
2. 就绪检查:
"能否处理流量?"
- 检查依赖
- 如果就绪返回200
- 用于负载均衡器
3. 深度健康检查:
"一切是否正常?"
- 全面检查
- 可能较慢
- 用于监控/调试
健康检查最佳实践
做:
- 保持存活检查简单快速
- 在就绪检查中检查所有关键依赖
- 在响应中包含版本/构建信息
- 返回适当状态码
不做:
- 在存活检查中阻塞依赖
- 在健康检查中包含重操作
- 暴露敏感信息
- 忘记处理依赖超时
测试韧性
如何测试
1. 单元测试:
- 隔离测试每个模式
- 模拟失败
- 验证行为
2. 集成测试:
- 测试模式组合
- 注入失败
- 验证恢复
3. 混沌工程:
- 在生产类似环境中测试
- 随机失败
- 验证系统行为
参见:chaos-engineering-fundamentals技能
实现考虑
库 vs 自定义
库(推荐):
- Polly (.NET)
- Resilience4j (Java)
- Hystrix (Java, 已弃用)
- go-resilience (Go)
好处:
- 经过实战检验
- 文档齐全
- 社区支持
- 内置指标
自定义实现:
- 仅当特定需求时
- 高维护负担
- 细微错误风险
监控韧性
跟踪指标:
断路器:
- 状态变化
- 打开持续时间
- 失败率
重试:
- 重试次数
- 重试成功率
- 最终成功/失败
舱壁:
- 并发调用
- 拒绝
- 队列深度
超时:
- 超时次数
- 延迟分布
最佳实践
1. 每个外部调用都需要超时
没有调用应该永远等待
2. 仅重试瞬态失败
不要重试400错误
3. 每个依赖一个断路器
不同服务需要不同保护
4. 舱壁关键路径
隔离重要与不重要
5. 计划回退
知道失败时该做什么
6. 监控一切
无法修复看不见的东西
7. 测试失败路径
快乐路径测试不够
相关技能
chaos-engineering-fundamentals- 测试韧性distributed-transactions- 处理事务中的失败rate-limiting-patterns- 控制请求速率