三层各重试三次,最底下的服务到底会收到多少请求?重试什么时候有用,什么时候只是添乱?加上网关的 proxy_next_upstream 又会怎样?这篇用本机起的小服务和 nginx,把放大倍数量出来,和公式对照。
重试层数 | 说明 | C 收到的调用 | 每个 client 请求 | 3 的 L 次方 -----+-----------+---------+--------------+--------- 0 | 没有重试层 | 20 | 1 | 1 1 | 1 层各试 3 次 | 60 | 3 | 3 2 | 2 层各试 3 次 | 180 | 9 | 9 3 | 3 层各试 3 次 | 540 | 27 | 27 4 | 4 层各试 3 次 | 1620 | 81 | 81 (连接类错误 0 次;不是超时,应为 0)
每一层单独看只多试 2 次,合起来是 3 的层数次方;C 完全不可用时每多一层乘 3。
C 的失败率 p | 实测:每个请求 C 收到的调用 | 公式 | 实测:client 拿到成功 | 公式 ---------+-----------------+------+----------------+------- 10% | 1.11 | 1.11 | 100.0% | 100.0% 30% | 1.45 | 1.43 | 100.0% | 100.0% 50% | 2.06 | 2.00 | 100.0% | 100.0% 90% | 9.88 | 9.42 | 92.8% | 94.2% (连接类错误 0 次;不是超时,应为 0) C 偶发失败时重试确实让成功率变高了(p = 30% 时单次调用成功率 70%,3 层重试后 600 个请求全部成功),代价是 C 多收到的调用;p 越高放大越厉害,而这时 C 恰恰最撑不住
失败率低时重试几乎不增加负载就把成功率拉到 100%;失败率越高放大越大,而这时 C 最撑不住。
| | 第 0 秒 | 1 | 2 | 3 | 4 | 5 | 6 | 7
----------+------------+-------+------+------+------+------+------+------+-----
没有重试层 | 收到的调用/s | 120 | 120 | 120 | 120 | 120 | 120 | 120 | 120
| client 成功率 | 100% | 100% | 48% | 71% | 99% | 100% | 99% | 99%
3 层各试 3 次 | 收到的调用/s | 120 | 155 | 1987 | 1202 | 120 | 120 | 120 | 127
| client 成功率 | 100% | 99% | 49% | 73% | 100% | 100% | 100% | 100%
(连接类错误 0 次;不是超时,应为 0)
触发的那 1.5 秒里,有重试层时 C 收到的调用是没有重试层时的十几倍;触发结束后的下一秒就接近恢复;这个实验里请求是同步重试的,重试不会在触发结束后继续累积,所以最终能恢复。负载更高(160/s)时,发请求的这个 Python 进程自己撑不住(大量连接错误),那一档的数字不可信,没有放进来C 容量降低的那一秒,重试把 C 收到的调用乘了十几倍,成功率却没有提高。
配置 | C 收到的调用 | 每个 client 请求 | client 最终看到的状态码
------------------------------------------------------+---------+--------------+------------------
client 试 1 次 / nginx 默认 | 60 | 3.0 | {503: 20}
client 试 3 次 / nginx 默认 | 180 | 9.0 | {503: 20}
client 试 1 次 / 加 http_503(上游 server 保持默认 max_fails=1) | 9 | 0.5 | {503: 1, 502: 19}
client 试 1 次 / 加 http_503 + max_fails=0 | 120 | 6.0 | {503: 20}
client 试 3 次 / 加 http_503 + max_fails=0 | 360 | 18.0 | {503: 20}
(连接类错误 0 次;不是超时,应为 0)
每一层的重试次数是相乘的:client 3 × nginx 换上游 2 × 服务 3 = 18;各层单独看都“只试几次”,合起来是放大的根源。第三行的另一个后果:http_503 让 nginx 把每个 503 记成一次失败,默认的 max_fails=1 / fail_timeout=10s 会把两个上游都摘掉,之后的请求是 nginx 自己回的 502(error log:no live upstreams),根本到不了服务nginx 的 proxy_next_upstream 又乘一层;加上 http_503 还会触发上游被摘除,出现 nginx 自己回的 502。