微服务中的超时、重试、熔断、限流和隔离应该如何配合?
简化版
超时限制等待,重试处理短暂故障,熔断避免持续攻击故障依赖,限流控制进入速率,隔离限制单个依赖占用的并发资源。它们必须围绕同一调用时间预算和幂等性协同,重复在多层开启重试或使用不合理顺序,反而会放大流量并拖垮系统。
详细版
先给端到端请求设截止时间,再给每次下游调用分配更短超时;仅对可重试异常和幂等操作做少量、带退避和抖动的重试。入口限流挡住过量请求,Bulkhead 隔离不同下游的线程或并发配额,CircuitBreaker 根据失败统计快速失败,最后才考虑有业务语义的降级。
组合顺序没有脱离实现的唯一答案:熔断器放在 Retry 外层时可能只记录最终结果,放在内层则可能把每次尝试都计入统计。必须根据实际装饰顺序、指标含义和总调用预算验证,而不是只看注解列表。
完整版教学
一、每种机制只解决一个问题
- Timeout:一次调用最多等多久。
- Retry:瞬时错误是否值得再试。
- CircuitBreaker:近期持续故障时是否继续调用。
- RateLimiter:单位时间允许多少请求进入。
- Bulkhead:某依赖最多占多少并发资源。
- Fallback:失败后业务返回什么。
限流控制速率,隔离控制并发,二者不能互相替代。每秒请求不高但每次卡很久,也可能把并发资源占满。
| 机制 | 保护对象 | 典型配置 | 主要反效果 |
|---|---|---|---|
| Timeout | 调用方等待时间 | 连接/读取/总 deadline | 只超时不取消,下游可能继续执行 |
| Retry | 短暂故障 | 次数、退避、抖动 | 放大流量、重复写入 |
| CircuitBreaker | 故障依赖隔离 | 失败率、慢调用率、半开探测 | 阈值不当导致误熔断或迟钝 |
| RateLimiter | 入口速率 | QPS、突发容量 | 误杀正常流量 |
| Bulkhead | 并发资源 | 信号量、线程池、队列 | 配额过小自造瓶颈 |
记忆钩子:超时管“等多久”,重试管“再试几次”,熔断管“还要不要碰它”,限流管“进来多少”,隔离管“最多占多少资源”。
二、先设计时间预算
如果网关总超时是 2 秒,服务内部就不能给三个串行下游各配置 2 秒。应从端到端截止时间倒推,为排队、网络、重试和响应预留预算,并尽可能传播剩余 deadline。
超时返回只说明调用方不再等待,不保证下游已经停止执行。写操作需要幂等键、取消支持或结果查询机制,防止调用方超时后重试造成重复提交。
用户请求总预算:2000ms
网关与鉴权:200ms
服务 A 自身处理:300ms
库存调用:500ms
支付预校验:600ms
预留网络抖动与序列化:400ms
如果库存调用失败后还要重试一次,那么单次库存超时就不能仍然设 500ms,还要为重试退避和后续步骤留时间。预算设计的核心不是“数字好看”,而是保证上游、当前服务和下游的等待边界一致。
三、重试为什么会放大故障
网关、服务 A 和服务 B 各重试 3 次时,最坏尝试次数可能成倍相乘。下游本来只是变慢,额外重试会继续增加队列,形成 retry storm。
重试应集中在最了解错误语义的一层,只处理连接瞬断、部分 5xx 等短暂故障,并使用指数退避和随机抖动。参数校验失败、权限失败和明确业务拒绝不应重试。
四、隔离舱保护资源
Bulkhead 给不同依赖设置独立并发上限,使慢库存服务不能占满订单服务全部线程或连接。Resilience4j 可提供信号量或线程池等隔离方式,响应式链路则要避免为每个请求盲目阻塞线程。
隔离配额太小会自造瓶颈,太大又失去保护作用;应结合下游连接池、平均耗时和允许排队时间确定。
五、熔断与限流的位置
入口限流通常应尽早拒绝无能力处理的流量,避免后续鉴权和路由成本;下游隔离在发起调用前获取许可。熔断器与重试谁在外层,要看希望统计每次尝试还是一次业务调用。
无论顺序如何,都要从指标验证:实际下游请求数、熔断失败率、重试次数、等待时间和被拒绝量是否符合设计。
六、降级不是吞异常
降级可以返回缓存、排队受理或关闭非核心功能,但不能对扣款失败返回成功。响应应让调用方知道数据是否陈旧、功能是否受限,并为每次降级记录指标。
如果没有安全的替代结果,快速失败就是正确策略。稳定性机制的目标是控制故障范围,不是把所有错误藏起来。
面试作答要避免把所有机制揉成一个词,可以按顺序讲:
- 先用总预算和超时限制等待;
- 再用幂等前提下的重试处理短暂故障;
- 然后用熔断、限流和隔离控制持续故障的扩散;
- 最后才讨论是否有业务可接受的降级结果。
七、常见误区与追问
- 误区:容错组件开得越多越稳定。 多层重复重试、过长超时和错误装饰顺序会放大故障,稳定性机制必须围绕同一预算协同。
- 误区:限流和隔离是一回事。 限流控制单位时间进入量,隔离控制并发资源占用,慢请求低 QPS 也可能打满并发。
- 追问:为什么重试必须看幂等性? 非幂等写操作重复执行可能造成重复下单、重复扣款,必须有幂等键或结果查询机制。
- 追问:熔断器放在 Retry 内外有什么差异? 放内层可能统计每次尝试,放外层可能只统计一次业务调用,指标和阈值含义不同。
- 误区:Fallback 就是返回空对象。 降级必须有业务语义,空对象可能掩盖真实故障并污染上游决策。
- 追问:为什么要加抖动? 多个实例同时按固定间隔重试会形成同步洪峰,随机抖动能打散流量。
八、加强记忆
先定总时间预算,再用单次超时止损;幂等短暂故障才重试,限流管速率、隔离管并发、熔断管持续故障,最后按业务降级。多层重复重试和不验证装饰顺序,是容错配置最危险的反效果。