bigywater.tech
微服务 · 4

重试预算:限制重试占总请求的比例

重试预算怎么限制“重试占总请求的比例”?它在失败率低的时候会不会妨碍重试?在多层叠加时能把放大压到多少?这篇用一个共享令牌桶,在本机的小服务上量出来。

1.099 对 3.000
服务完全不可用时的放大:预算 10% 对 不限(共 3 次)
88.3% / 96.0% / 99.3%
失败率 20% 时预算 10% / 预算 20% / 不限的成功率(放大 1.100 / 1.197 / 1.237)
27.00 → 1.31
三层叠加、C 完全不可用时,每个原始请求让 C 收到的调用数:每层不限 对 每层预算 10%

预算 × 失败率

实测 · 本机执行输出
  服务失败率 p | 重试策略      | 成功     | 放大(服务收到 ÷ 原始请求) | 服务收到的调用
  --------+-----------+--------+-----------------+--------
  5%      | 不重试       | 95.5%  | 1.000           | 1500   
  5%      | 预算 10%    | 100.0% | 1.047           | 1571   
  5%      | 预算 20%    | 100.0% | 1.047           | 1571   
  5%      | 预算 50%    | 100.0% | 1.047           | 1571   
  5%      | 不限(共 3 次) | 100.0% | 1.047           | 1571   
  20%     | 不重试       | 79.9%  | 1.000           | 1500   
  20%     | 预算 10%    | 88.3%  | 1.100           | 1650   
  20%     | 预算 20%    | 96.0%  | 1.197           | 1796   
  20%     | 预算 50%    | 99.7%  | 1.242           | 1863   
  20%     | 不限(共 3 次) | 99.3%  | 1.237           | 1856   
  50%     | 不重试       | 51.1%  | 1.000           | 1500   
  50%     | 预算 10%    | 56.5%  | 1.100           | 1650   
  50%     | 预算 20%    | 61.1%  | 1.199           | 1799   
  50%     | 预算 50%    | 76.1%  | 1.500           | 2250   
  50%     | 不限(共 3 次) | 87.9%  | 1.730           | 2595   
  100%    | 不重试       | 0.0%   | 1.000           | 1500   
  100%    | 预算 10%    | 0.0%   | 1.099           | 1649   
  100%    | 预算 20%    | 0.0%   | 1.199           | 1799   
  100%    | 预算 50%    | 0.0%   | 1.500           | 2250   
  100%    | 不限(共 3 次) | 0.0%   | 3.000           | 4500   
  (连接类错误 0 次;不是超时,应为 0)

失败率低时预算几乎不妨碍重试;失败率高时放大被压在 1 + ratio 附近,代价是少救回来一些请求。

容量有限时

实测 · 本机执行输出
            |            | 第 0 秒 | 1    | 2   | 3   | 4    | 5    | 6    | 7   
  ----------+------------+-------+------+-----+-----+------+------+------+-----
  不重试       | 收到的调用/s    | 120   | 120  | 120 | 120 | 120  | 120  | 120  | 120 
            | client 成功率 | 100%  | 100% | 48% | 70% | 100% | 100% | 99%  | 100%
  不限(共 3 次) | 收到的调用/s    | 120   | 120  | 254 | 190 | 123  | 120  | 122  | 120 
            | client 成功率 | 100%  | 100% | 48% | 71% | 100% | 100% | 99%  | 100%
  预算 10%    | 收到的调用/s    | 120   | 120  | 141 | 128 | 120  | 120  | 120  | 120 
            | client 成功率 | 100%  | 100% | 48% | 71% | 100% | 100% | 100% | 100%
  (连接类错误 0 次;不是超时,应为 0)

只有一层重试时放大本来就有限,预算把触发期间多出来的调用压得更少,成功率不变。

多层叠加

实测 · 本机执行输出
  策略          | C 收到的调用 | 每个原始请求
  ------------+---------+-------
  每层不限(共 3 次) | 5400    | 27.00 
  每层预算 10%    | 263     | 1.31  
  每层预算 10% 时,放大的上界是每层 1.1 倍,三层是 1.1³ = 1.33;实测值在这附近(桶开始时是空的,所以略低于上界)
  (连接类错误 0 次;不是超时,应为 0)

每层各有预算,放大不再相乘,上限是各层 1 + ratio 的乘积。

分步演示

重试预算
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。本篇的服务是 Python 3.14 http.server(线程化)在本进程里起的;预算是我写的共享令牌桶(每个原始请求存 ratio 个令牌、桶上限 10、初始为空、每次重试取 1 个),不是任何库的实现;“不限”是每个请求最多共 3 次;第一节每种组合 1500 个请求跑一轮;实验里连接类错误 0 次
    • 没有验证:Finagle / gRPC 的重试节流各自的参数(时间窗口、每秒最少重试数)——这里的桶没有时间衰减;预算在多实例 client 之间不共享时的总放大(每个 client 实例各一个桶);预算与熔断同时使用时的相互作用;更高的并发和真实网络
    京ICP备2026067502号-1京公网安备11010502063487号