一条调用链上,每一跳的超时该设多少?固定超时、传播剩余预算、重试和排队分别会发生什么?这篇用本机起的几个小 HTTP 服务,把“预算”在多跳之间怎么被吃掉、被浪费量出来。
策略 | client 1 s 内拿到结果 | client 超时 | 拿到 504 | C 花在干活上的总时间 | C 把活干完的次数 | 干完时 client 早已放弃的次数 -----------------+------------------+-----------+--------+-------------+-----------+------------------- 每一跳固定 1000 ms 超时 | 126 | 70 | 4 | 123.6 s | 200 | 74 传播剩余预算 | 126 | 0 | 74 | 73.9 s | 126 | 0 这 200 个请求里按随机数事先数出来有 74 个会让 C 慢(1500 ms);1500 ms 的活在 1000 ms 的预算里本来就做不完,所以不论什么策略,这 74 个都拿不到结果;区别是 C 要不要把它们干完
两种策略的成功数一样,差别在下游:固定超时的下游不知道 client 已经走了,会把活干完;传播剩余预算后下游可以提前停下。
B 的策略 | client 在 1 s 内得到 B 的回复(504) | client 超时(什么也没等到) | client 看到的延迟中位数 | C 一共收到的调用 | 其中 client 放弃之后才发来的 --------------------+-----------------------------+-------------------+-----------------+-----------+------------------- 固定:每次 500 ms,最多 3 次 | 0 | 40 | 1002 ms | 120 | 40 每次 ≤ 剩余预算 | 40 | 0 | 985 ms | 80 | 0
总耗时 = 次数 × 每次超时。不看剩余预算,最后一次尝试会在 client 放弃之后才发出。
策略 | 1 s 内成功 | 成功率 | 成功请求延迟中位数 | 成功请求延迟 p99 -----------------+-----------+-------+-----------+----------- 预算平分:每跳 333 ms | 350 / 400 | 87.5% | 316 ms | 337 ms 共用剩余预算:每跳最多用掉剩下的 | 393 / 400 | 98.2% | 315 ms | 823 ms 按随机数事先数出来:至少一跳慢的请求 50 个(平分必败),至少两跳慢的请求 7 个(共用预算也会超过 1000 ms:600 + 600 > 1000)
平分预算让任何一跳变慢都必败;共用剩余预算把前面省下的时间留给后面,代价是成功请求的 p99 更高。
策略 | 400 ms 内成功 | client 超时(或晚于 400 ms) | 收到 504 | worker 实际处理的请求 | 处理完时已超过截止时刻的 | 直接丢掉的 ----------+------------+-----------------------+--------+----------------+--------------+------ 来者不拒 | 34 | 366 | 0 | 313 | 279 | 0 出队时丢掉已过期的 | 162 | 62 | 176 | 224 | 54 | 176 来者不拒时,队列越排越长,排到的请求已经过了截止时间,worker 白干;出队时检查截止时间,把时间花在还有人在等的请求上
队列越长,排到的请求越是没人在等。出队时检查剩余时间,把容量留给还有人等的请求。