给服务间 REST 调用加上超时与重试
目标
针对不稳定的下游,直接实现超时、指数倒退、震动、禁止重试条件、重置,完成同步调用的基本技能。
为什么重要
在同步调用中,错误通常归因于“没有超时”或“错误地重试”。如果没有超时,当下游变慢时,上游的连接池会先用完,甚至连与下游无关的请求也会失败。相反,如果无限地以固定的间隔重试,每次下游试图恢复时都会被同样的波浪覆盖。在实际案例中,一个订单实例每秒处理20多个请求,以maxAttempts 5重试后,支付服务每秒处理了10,000个请求。重试不是增加负载,而是乘以负载。因此,这个练习重视输入值的顺序——首先是超时,然后是重试条件,然后是后退和震动,最后是恢复。
阶段
/opt/app/flaky.py将 127.0.0.1:8110 启动。GET /fast要去200。/root/rest/call.py制作/fast呼叫并回复正文/root/rest/fast.out保存到。/root/rest/timeout.py制作/slow以1.0秒超时调用。/root/rest/timeout.out在第一行TIMEOUT,在第二行elapsed=<초>写。时间不得少于2.0秒。/root/rest/retry.py制作/flaky?key=lab&fail=2重新尝试了95%的概率,最终成功了。/root/rest/retry.log在attempt=<n> wait=<초>以形式为准,每次尝试留下一行,共3行。retry.py将等待计算改为full jitter。抽取尝试编号2的等待时间20次/root/rest/jitter.txt逐行写一行。不同值必须超过15个。/root/rest/retry400.py罗/bad呼叫。400不会再尝试,所以/root/rest/retry400.log必须是准确的1行。/root/rest/budget.txt在instances=20,rps=100,max_attempts=5,worst_rps=10000写四行。/root/rest/gateway.py将 127.0.0.1:8111 启动。GET /order是下游/always500呼叫,但适用超时和重新尝试,最终失败时200和{"degraded":true}给。
参考
- 超时值从对方的p99开始。如果设定为p99的10倍就相当于没有了。
- full jitter:
wait = random.uniform(0, min(cap, base * 2 ** attempt)) - 常见的错误1:只留下成功重试日志的尝试——即使失败的尝试也要留下,才能计算预算。
- 常见的错误2:不把超时作为例外,只留下堆栈跟踪,无法计时。
不稳定的下游浮动
/opt/app/flaky.py将 127.0.0.1:8110 启动。GET /fast要去200。
执行/opt/app/flaky.py后会显示8110。请先用眼睛确认/fast、/slow、/flaky、/bad四个路线的运行情况。
以正常呼叫为基准线设定基准线
/root/rest/call.py制作/fast呼叫并回复正文/root/rest/fast.out保存到。
httpx 使用 urllib 调用 /fast,并将正文保存为文件。现在不用担心超时。
将缓慢的响应以超时方式中断
/root/rest/timeout.py制作/slow以1.0秒超时调用。/root/rest/timeout.out在第一行TIMEOUT,在第二行elapsed=<초>写。时间不得少于2.0秒。
/slow会在3秒后回复。如果将客户端超时时间设置为1秒,就会发生异常。捕获异常,并一起记录结果和持续时间。
实现指数百折扣重新尝试
/root/rest/retry.py制作/flaky?key=lab&fail=2重新尝试了95%的概率,最终成功了。/root/rest/retry.log在attempt=<n> wait=<초>以形式为准,每次尝试留下一行,共3行。
每次尝试的间隔都翻倍。每个尝试的编号和等待时间都必须在日志中留下一行才能得分。
用震动重新尝试时点的分散
retry.py将等待计算改为full jitter。抽取尝试编号2的等待时间20次/root/rest/jitter.txt逐行写一行。不同值必须超过15个。
full jitter等待从0开始计算的backoff之间的随机数。即使在同一尝试编号下,值也必须每次都不同。
对于确定的错误,不要再试一次。
/root/rest/retry400.py罗/bad呼叫。400不会再尝试,所以/root/rest/retry400.log必须是准确的1行。
400无论发送多少次都是400。请插入状态代码来区分是否重新尝试的分支。
重新尝试预算计算
/root/rest/budget.txt在instances=20,rps=100,max_attempts=5,worst_rps=10000写四行。
实例数x每秒请求数x最大尝试数是下游接收的最差每秒请求数。请分别写下三个值和结果。
综合为有翻盖的门户
/root/rest/gateway.py将 127.0.0.1:8111 启动。GET /order是下游/always500呼叫,但适用超时和重新尝试,最终失败时200和{"degraded":true}给。
即使下游死机,也给用户200,但会在响应中留下质量下降的标记。同时写入前一步的超时和重试。