bigywater.tech
微服务 · 7

熔断与超时、重试的相互作用:配置冲突案例

熔断、超时、重试放在一起,哪些组合会互相抵消?重试放在熔断器的里面还是外面?超时设得比下游的尾延迟还短会怎样?这篇用本机的小服务量出四个典型的配置冲突案例。

15 对 5
下游一直 503 时,重试放在熔断器里面与外面,熔断器打开之前下游收到的调用数
15.01 s
调用超时 3 s、1 个线程时,熔断器连续失败 5 次才打开用的时间(20 个线程时是 3.01 s)
16 次
下游完全健康、超时 100 ms 时,20 秒里熔断器打开的次数(超时 1 s 是 0 次)

重试放在熔断器的哪一侧

实测 · 本机执行输出
  放法                | 熔断器打开       | 到达下游的逻辑请求数 | 下游收到的调用 | 被熔断器拒绝的尝试
  ------------------+-------------+------------+---------+----------
  重试在熔断器里面(熔断器包着重试) | 第 5 个逻辑请求之后 | 5          | 15      | 7        
  重试在熔断器外面(重试包着熔断器) | 第 2 个逻辑请求之后 | 2          | 5       | 11       

放在外面时每次尝试各记一次失败,熔断器打开得更快,下游挨的调用更少。

超时与熔断器的反应时间

实测 · 本机执行输出
  调用超时  | 20 线程:打开用了多久 | 到达下游的调用 | 被拖住的线程时间 | 1 线程:打开用了多久 | 到达下游的调用 | 被拖住的线程时间
  ------+--------------+---------+----------+-------------+---------+---------
  0.2 s | 0.21 s       | 24      | 4.9 s    | 1.01 s      | 5       | 1.0 s   
  1 s   | 1.00 s       | 24      | 24.1 s   | 5.02 s      | 5       | 5.0 s   
  3 s   | 3.01 s       | 24      | 72.1 s   | 15.01 s     | 5       | 15.0 s  
  (连接类错误 0 次;不是超时,应为 0)

失败要等超时才记录,超时越长熔断器越晚知道,被拖住的线程时间与超时成正比。

重试碰上已经打开的熔断器

实测 · 本机执行输出
  策略       | 拿到失败的延迟中位数 | p99     
  ---------+------------+---------
  熔断器打开也重试 | 370.1 ms   | 379.1 ms
  熔断器打开就放弃 | 0.0 ms     | 0.0 ms  

熔断器打开是快速失败,再退避重试只会白等。

超时短过尾延迟

实测 · 本机执行输出
  调用超时    | 熔断器打开的次数 | 熔断器处于打开的时间(次数 × 1 s) | 发出的调用 | 其中拿到 200 | 其中超时 | 服务端白干(客户端已超时仍处理完)
  --------+----------+----------------------+-------+----------+------+------------------
  100 ms  | 16       | 16 s / 20 s          | 974   | 768      | 206  | 206              
  1000 ms | 0        | 0 s / 20 s           | 2210  | 2210     | 0    | 0                
  (连接类错误 0 次;不是超时,应为 0)
  下游完全健康,但超时 100 ms 把 20% 的慢请求全算成失败,熔断器反复打开;被拒的请求本来都能成功(打开期间调用方什么也拿不到)。超时要比下游的正常尾延迟长

健康下游的慢请求被当作失败,熔断器反复打开,打开期间所有请求都拿不到结果。

分步演示

熔断与超时、重试的相互作用
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。熔断器是第 5 篇里的手写版(连续失败 5 次打开、半开最多放行 3 个、3 个连续成功关闭);第四节为了简单用了一个更小的计数窗口熔断器(最近 50 次、失败率 ≥ 10% 打开,打开 1 s 后直接关闭并清空窗口,没有半开);下游是本进程里的 Python http.server;实验里连接类错误 0 次
    • 没有验证:Resilience4j、Hystrix、Polly 等库对“重试与熔断器谁在外面”的默认顺序;并发比 20 更高的情形;重试带抖动和重试预算时的组合(见第 3、4 篇);服务端排队时超时与队列等待的叠加
    京ICP备2026067502号-1京公网安备11010502063487号