重试为什么要加抖动?AWS 提出的 Full、Equal、Decorrelated 三种抖动,等待时间的分布有什么不同?在容量有限的服务面前,200 个 client 一起重试会怎样?这篇用本机的小服务把它们量出来。
算法 | n=0 | n=1 | n=2 | n=3 | n=4 | n=5 | n=6 --------------------+---------------+---------------+----------------+----------------+------------------+------------------+----------------- 固定间隔 | 100 (100~100) | 100 (100~100) | 100 (100~100) | 100 (100~100) | 100 (100~100) | 100 (100~100) | 100 (100~100) 指数退避(无抖动) | 100 (100~100) | 200 (200~200) | 400 (400~400) | 800 (800~800) | 1600 (1600~1600) | 3200 (3200~3200) | 3200 (3200~3200) Full Jitter | 50 (5~95) | 100 (10~190) | 199 (20~380) | 402 (40~761) | 795 (78~1513) | 1597 (154~3034) | 1601 (164~3031) Equal Jitter | 75 (53~97) | 150 (105~195) | 300 (210~390) | 601 (420~781) | 1197 (839~1556) | 2399 (1677~3117) | 2400 (1682~3116) Decorrelated Jitter | 200 (110~290) | 350 (122~694) | 572 (133~1419) | 891 (143~2686) | 1191 (154~3200) | 1426 (163~3200) | 1592 (177~3200) 括号里是 5%~95% 分位。无抖动时每个 client 的等待完全相同(区间宽度为 0);Full Jitter 的下限是 0,所以有的 client 几乎立刻重试;Equal Jitter 的下限是一半;Decorrelated 依赖上一次的等待
无抖动时所有 client 的等待完全相同;Full 的下限接近 0,Equal 的下限是一半,Decorrelated 依赖上一次的等待。
算法 | 轮数 | 服务端收到的请求总数(中位数,括号是范围) | 平均每个 client 试几次 | 最忙的 100 ms 窗口收到 | 完成时间中位数 | p99 | 最后一个完成 --------------------+----+-----------------------+-----------------+-----------------+------------------------+------------------------+----------------------- 固定间隔 | 1 | 2667 | 13.3 | 200 | 1.29 s | 3.01 s | 3.02 s 指数退避(无抖动) | 1 | 2314 | 11.6 | 200 | 19.25 s | 64.22 s | 67.39 s Full Jitter | 3 | 1066 (1065~1069) | 5.3 (5.3~5.3) | 317 (305~320) | 1.08 s (1.06 s~1.10 s) | 4.19 s (4.18 s~4.24 s) | 4.99 s (4.49 s~5.00 s) Equal Jitter | 3 | 1034 (1024~1038) | 5.2 (5.1~5.2) | 228 (214~240) | 1.30 s (1.30 s~1.32 s) | 5.38 s (5.35 s~5.42 s) | 5.67 s (5.50 s~5.81 s) Decorrelated Jitter | 3 | 822 (797~825) | 4.1 (4.0~4.1) | 200 (200~200) | 1.06 s (1.05 s~1.08 s) | 2.99 s (2.69 s~3.37 s) | 3.48 s (3.13 s~4.69 s) 服务端每个 100 ms 窗口收到的请求数(从 0 开始的前 20 个窗口,取第一轮): 算法 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 --------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----+----+----+----+----+----+--- 固定间隔 | 200 | 200 | 200 | 200 | 191 | 133 | 134 | 141 | 143 | 133 | 118 | 115 | 111 | 99 | 87 | 75 | 66 | 58 | 47 | 16 指数退避(无抖动) | 200 | 200 | 0 | 200 | 0 | 0 | 0 | 129 | 59 | 0 | 0 | 0 | 0 | 0 | 0 | 94 | 82 | 0 | 0 | 0 Full Jitter | 317 | 203 | 125 | 68 | 69 | 54 | 37 | 26 | 24 | 20 | 22 | 19 | 11 | 6 | 7 | 3 | 5 | 5 | 3 | 8 Equal Jitter | 228 | 173 | 156 | 43 | 33 | 94 | 60 | 5 | 1 | 9 | 37 | 40 | 31 | 28 | 2 | 0 | 0 | 0 | 2 | 7 Decorrelated Jitter | 200 | 46 | 105 | 85 | 52 | 53 | 42 | 35 | 34 | 30 | 26 | 20 | 16 | 15 | 8 | 10 | 7 | 7 | 5 | 3
无抖动的指数退避让请求一波一波地同时到,容量有限时一波只能放过几个;加抖动后请求数降到不到固定间隔的一半。
参数 | 服务端收到的请求总数 | 最忙的 100 ms 窗口收到 | 完成时间中位数 | 最后一个完成 --------------------------+------------+-----------------+---------+------- base 20 ms / cap 3200 ms | 1499 | 544 | 1.05 s | 4.78 s base 100 ms / cap 3200 ms | 1074 | 315 | 1.09 s | 4.56 s base 400 ms / cap 3200 ms | 729 | 234 | 1.05 s | 5.17 s base 100 ms / cap 400 ms | 1380 | 322 | 1.00 s | 1.97 s base 100 ms / cap 800 ms | 1117 | 334 | 1.02 s | 2.50 s (连接类错误 0 次;不是超时,应为 0)
base 太小退避没拉开;cap 小则最后一个更早完成,代价是请求更多。