Envoy 的重试该怎么配?重试时要不要避开已经试过的主机?per_try_timeout 能解决慢主机吗?熔断器的阈值溢出时什么样?异常点检测什么时候摘除、为什么摘除不了所有坏主机?这篇用 Envoy 1.39.3 和已知行为的后端逐项量出来。
策略 | 客户端看到的成功率 | 后端收到的请求数 ÷ 客户端请求数 | Envoy 记录的重试次数 | 重试成功次数 | bad 收到的请求数 -------------------+-----------+-------------------+---------------+--------+----------- (a)不重试 | 75.5% | 1.00 | 0 | 0 | 1000 (b)重试 1 次,避开已试过的主机 | 100.0% | 1.28 | 570 | 570 | 1157 (c)重试 1 次,不避开 | 94.0% | 1.25 | 500 | 379 | 1250 重试把一半请求的失败(轮询到 bad 并且 bad 这次失败,约 25%)变成成功,代价是后端多收到请求;重试时避开已经试过的主机,重试几乎都落在 good 上;不避开时重试仍有机会选到 bad 并再次失败,成功率低一些
重试能把失败变成成功,代价是后端多收到请求;避开已试过的主机比不避开更有效。
配置 | 成功率 | 延迟 p25(ms) | p50(ms) | p75(ms) | p99(ms) | 延迟超过 150 ms 的请求比例 | slow 后端收到的请求数 ----------------------------------+------+------------+---------+---------+---------+-------------------+-------------- (a)只有总超时 2 s | 100% | 2 | 505 | 1008 | 1013 | 50% | 100 (b)per_try_timeout 0.2 s + 重试 1 次 | 100% | 3 | 209 | 220 | 229 | 66% | 133 没有 per_try_timeout 时,轮询落到 slow 的请求要等满 1 s;设了 per_try_timeout 之后,第一次尝试 0.2 s 超时就换到 fast 重试,这些请求的延迟变成 0.2 s 多一点,所以 p99 从约 1 s 降到 0.2 s 多;代价是要等满 per_try_timeout 的请求仍然比较多,slow 后端收到的请求数也比 (a) 多——重试本身会让 Envoy 的轮询游标多走几步,我的猜测是这让更多请求第一次落到 slow 上(这个猜测没有另外验证,每次运行具体数字略有不同);超时只是 Envoy 不再等,slow 后端并没有因此少收请求
它缩短了单个请求的等待时间,但不减少慢主机收到的请求。
配置 | 200 | 503 | 带 x-envoy-overloaded 头的 | 最慢的请求耗时(ms) | upstream_rq_pending_overflow | upstream_cx_overflow | 后端实际收到的请求数 --------------------------------------------+-----+-----+-------------------------+-------------+------------------------------+----------------------+----------- (a)默认阈值 | 20 | 0 | 0 | 316 | 0 | 0 | 20 (b)max_connections 2、max_pending_requests 2 | 4 | 16 | 16 | 627 | 16 | 4 | 4 上游是 HTTP/1.1 时一个连接同时只处理一个请求,超过 max_connections 的请求进入等待队列,等待队列也满了就立即被拒绝(503,响应头带 x-envoy-overloaded);熔断的作用是在上游已经跟不上时快速失败,而不是把更多请求堆在上游前面
连接数和等待队列满了之后,多出的请求立即返回 503(x-envoy-overloaded)。
(a)h3 坏,max_ejection_percent 50:
时间段 | 请求数 | 200 | 500 | 500 的比例
--------+-----+-----+-----+--------
0~3 s | 30 | 27 | 3 | 10%
3~6 s | 30 | 30 | 0 | 0%
6~9 s | 30 | 25 | 5 | 17%
9~12 s | 30 | 29 | 1 | 3%
12~15 s | 30 | 30 | 0 | 0%
15~18 s | 30 | 30 | 0 | 0%
18~21 s | 30 | 28 | 2 | 7%
21~24 s | 30 | 26 | 4 | 13%
Envoy 的异常点检测统计:ejections_enforced_total = 3,ejections_active(结束时)= 1,ejections_consecutive_5xx = 3;lb_healthy_panic(进入 panic 模式后被选主机的请求数)= 0
(b)h2、h3 都坏,max_ejection_percent 100:
时间段 | 请求数 | 200 | 500 | 500 的比例
--------+-----+-----+-----+--------
0~3 s | 30 | 11 | 19 | 63%
3~6 s | 30 | 10 | 20 | 67%
6~9 s | 30 | 13 | 17 | 57%
9~12 s | 30 | 12 | 18 | 60%
12~15 s | 30 | 10 | 20 | 67%
15~18 s | 30 | 12 | 18 | 60%
18~21 s | 30 | 11 | 19 | 63%
21~24 s | 30 | 12 | 18 | 60%
Envoy 的异常点检测统计:ejections_enforced_total = 6,ejections_active(结束时)= 2,ejections_consecutive_5xx = 6;lb_healthy_panic(进入 panic 模式后被选主机的请求数)= 163
(a)刚开始 h3 收到约三分之一的请求并且全部失败,连续 3 次 5xx 之后 h3 被摘除,失败消失;6 s 的摘除时间到了之后 h3 被放回来,又收到请求、失败,再被摘除;这一次摘除之后要到约 18 s 才再有失败,比 6 s 的 base_ejection_time 长得多(摘除时间随摘除次数增加是我对文档的了解,表里的先后是实测)。(b)两个坏主机被摘除之后只剩 1 个健康主机,健康比例低于默认的 panic 阈值,按 lb_healthy_panic 的计数看 Envoy 进入了 panic 模式——忽略摘除状态,把请求分给所有主机,坏主机重新收到流量,失败的比例和没有异常点检测时差不多(约三分之二)
ERRORS {}连续 5xx 会摘除主机;健康主机比例低于 panic 阈值时,Envoy 会忽略摘除状态。