概述
依赖完全宕机反而容易处理:熔断打开、流量停止,所有人都能理解。真正困难的是 30% 的请求失败,因为每个请求看起来都还有成功的可能。重试似乎合理,但在 30% 失败率下重试三次会把一次用户请求放大成四次上游调用,把已经吃力的服务彻底压垮。需要设计的是降级,而不是完美。
步骤
- 用取模计数让失败率精确且可复现:
let call = 0;
if (++call % 10 < 3) return res.writeHead(500).end('injected');
- 设置重试预算:每个用户请求最多两次上游调用,并加入抖动。
- 增加熔断器:
const breaker = new CircuitBreaker(search, {
timeout: 1000,
errorThresholdPercentage: 50,
volumeThreshold: 10,
resetTimeout: 5000,
});
- 熔断打开时返回降级结果:缓存数据、
degraded: true与Warning响应头,而不是把错误直接抛给用户。 - 关闭故障注入,等待
resetTimeout,确认探活成功后熔断器关闭。
验证
注入开始后,前十个请求中超过一半失败,熔断器应打开,后续请求立即拒绝而不是等待超时。降级响应应为 200 且带 degraded: true,错误率保持在 SLO 之内。关闭注入并等待 6 秒后,真实依赖恢复,熔断器回到关闭状态且无需重启。
注意事项
- 随机注入会产生不稳定测试,应使用确定性序列。
- 没有重试预算的熔断器仍会产生突发流量,必须先限制每请求的上游调用次数。
- 写操作重试需要幂等键,超时不等于失败。
- 熔断状态是进程级的,集群中可能部分实例打开、部分关闭。
- 降级必须有正确的缓存或兜底数据,注入只能证明路径被执行,不能证明数据正确。