bigywater.tech
微服务 · 15

慢节点与对冲请求:p99 被一个慢实例拉高,用冗余换尾延迟

一个慢实例怎么拉高整个服务的 p99?对冲请求(hedging)能压多少尾延迟,又要多发多少请求?负载高的时候会怎样?这篇用 5 个副本的模型量出来。

74 → 178 ms
5 个副本里有 1 个慢节点(处理时间 3 倍)时,p99 的变化;p50 是 47 → 49 ms
434 → 158 ms
偶发长尾(3% 的请求慢 10 倍)、100/s 时,对冲 100 ms 把 p99 压下来,额外请求只有 3.4%
1876 → 5754 ms
400/s(接近容量)时,对冲反而让 p99 变差,额外请求 97.7%

慢节点

实测 · 本机执行输出
  情况              | p50   | p90    | p99    | p99.9  | 发给副本 0 的请求比例
  ----------------+-------+--------+--------+--------+-------------
  全部正常 · 随机       | 47 ms | 63 ms  | 74 ms  | 92 ms  | 18%         
  1 个慢节点 · 随机     | 49 ms | 128 ms | 178 ms | 211 ms | 17%         
  1 个慢节点 · 挑在途最少的 | 48 ms | 96 ms  | 161 ms | 177 ms | 10%         

一个慢节点把 p99 拉高而中位数几乎不变;挑在途最少的副本能少发一些给它,但不能消除。

对冲的 delay

实测 · 本机执行输出
  对冲 delay | p50   | p99    | p99.9  | 额外请求 
  ---------+-------+--------+--------+------
  不对冲      | 49 ms | 181 ms | 230 ms | 0.0% 
  60 ms    | 50 ms | 135 ms | 145 ms | 27.8%
  100 ms   | 49 ms | 160 ms | 164 ms | 15.0%
  150 ms   | 48 ms | 180 ms | 204 ms | 2.6% 
  250 ms   | 49 ms | 185 ms | 216 ms | 0.0% 

delay 越短尾延迟越低,额外请求越多;delay 比大多数请求耗时还短时额外请求接近三成。

高负载下的对冲

实测 · 本机执行输出
  到达速率  | 对冲     | p50     | p99     | p99.9   | 额外请求 
  ------+--------+---------+---------+---------+------
  100/s | 不对冲    | 47 ms   | 434 ms  | 593 ms  | 0.0% 
  100/s | 100 ms | 47 ms   | 158 ms  | 181 ms  | 3.4% 
  250/s | 不对冲    | 54 ms   | 505 ms  | 593 ms  | 0.0% 
  250/s | 100 ms | 54 ms   | 162 ms  | 220 ms  | 9.5% 
  400/s | 不对冲    | 482 ms  | 1876 ms | 2907 ms | 0.0% 
  400/s | 100 ms | 3566 ms | 5754 ms | 7747 ms | 97.7%

排队让大多数请求超过 delay,几乎每个请求都发第二份,把容量吃掉,延迟比不对冲更差。

分步演示

慢节点与对冲请求
本步的对象

    本文用到的工具与边界

    • 本机 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 个副本和 client 都在本进程里,请求是函数调用(没有网络),每个副本是一个 4 并发的信号量加 sleep(约 40 ms × 对数正态噪声 σ 0.25);慢节点 3 倍;长尾是 3% 的请求慢 10 倍;对冲时输掉的那一份不取消、仍会处理完;每种配置只跑了一轮(约 500 到 2000 个请求),p99.9 的样本很少;最初我把慢节点设成 10 倍,慢副本自己的容量(20/s)低于分到的流量,队列无限增长,p99 变成十几秒,那不是“慢节点”而是“过载的节点”,所以改成 3 倍并降低了速率
    • 没有验证:真实网络上的对冲(请求取消、连接复用);对冲 delay 取在线统计的 p95(这里是固定值);gRPC 的 hedging 配置(见 gRPC 系列);服务端能取消排队中的请求时(tied requests)额外负载会更小;更高并发和更多副本
    京ICP备2026067502号-1京公网安备11010502063487号