LabHub
学习 学习路径 课程

系统间对接 (EAI)

实现指数退避、抖动与 DLQ

在 LabHub 中继续学习

目标

实现带指数退避和抖动的重试,区分禁止重试的错误,并创建 DLQ 加载、重新处理脚本和重试策略表。

为什么重要

重试并非没有成本。网关重试 4 次 × 服务 A 重试 4 次 × 服务 B 重试 4 次,会让用户的一次点击向最终系统发出64 次请求。正在恢复的对方系统会被这轮轰炸再次击倒。此外,没有抖动的指数退避会让 1 万个客户端在完全相同的时刻重试,这就是 thundering herd。由于平时不会出现问题,往往直到真正的大规模故障发生时才首次发现。这个实习要求把重试当作需要设计的机制,而不是简单讨论“加了还是没加”。

步骤

  1. 启动不稳定 API。 python3 /opt/lab/fixtures/eai/rest/flaky_api.py 9300(后台运行)
    • /flaky?key=<키>:相同键的前 3 次调用返回 503,从第 4 次起返回 200
    • /bad:始终返回 400
    • /dead:始终返回 503
  2. 调用一次 /flaky?key=t1,把结果保存到 /root/r/first.txt。文件中必须有一行 http_code=503
  3. 创建 /root/r/retry.sh。接收两个参数(URL 최대시도수),以固定间隔重试,并在最后一行输出 result=<ok|fail> attempts=<n>。成功时退出码为 0。
  4. 创建 /root/r/backoff.sh。接收两个参数(URL 최대시도수),按指数退避(1、2、4、8 秒,上限 30 秒)重试。
    • 每次尝试前输出一行 attempt=<n> sleep=<초>
    • 最后一行输出 result=<ok|fail> attempts=<n>
    • 环境变量为 DRY=1 时,不实际等待,只输出计划
  5. backoff.sh 添加抖动。sleep 值必须是**计算值的 50% 以上、100% 以下或等于 100%**的随机值,并且以 DRY=1 运行两次时,数值必须不同。
  6. backoff.sh 区分禁止重试的错误。收到 HTTP 400/401/403/404/409 时立即停止,并以 attempts=1 结束。(使用 /bad 确认。)
  7. 创建 /root/r/senddlq.sh。接收两个参数(URL 메시지ID),重试超过最大次数时创建 /root/r/dlq/<메시지ID>.json。JSON 必须包含 msg_id, url, reason, attempts, first_failed_at 五个键。(url 是第 7 步重新处理时再次调用的目标。)使用 /dead 和消息 ID M-001 运行并创建文件。(使用同一 ID 再次运行时覆盖同一文件。)
  8. 创建 /root/r/dlq-replay.sh。接收一个参数(DLQ 目录),对其中每个 .json 尝试重新处理;失败时将该文件的 attempts 增加 1 后重新保存。最后一行输出 replayed=<시도수> succeeded=<성공수>。目录不存在时以非 0 退出码结束。
  9. 创建 /root/r/policy.csv。第一行为 error,retry,max_attempts,backoff,final。必须包含以下六种类型:connection-refused, timeout, http-500, http-400, http-401, http-429retryY/N/조건부finalDLQ/중단/통보 之一。

参考

重现失败

调用一次 /flaky?key=t1,把结果保存到 /root/r/first.txt。文件中必须有一行 http_code=503

创建重试机制之前,首先要观察失败是什么样子。请同时确认 HTTP 状态码和响应正文。

固定间隔重试

创建 /root/r/retry.sh。接收两个参数(URL 최대시도수),以固定间隔重试,并在最后一行输出 result=<ok|fail> attempts=<n>。成功时退出码为 0。

重试次数必须通过参数接收,脚本才能复用。成功时立即退出,并输出第几次尝试成功。

指数退避

创建 /root/r/backoff.sh。接收两个参数(URL 최대시도수),按指数退避(1、2、4、8 秒,上限 30 秒)重试。

实际等待会使评分变慢。请先输出计划的等待时间,并在试运行模式下不要真正休眠。

添加抖动

backoff.sh 添加抖动。sleep 值必须是**计算值的 50% 以上、100% 以下或等于 100%**的随机值,并且以 DRY=1 运行两次时,数值必须不同。

大量客户端遵循相同规则时会同时重试。请在计算值中加入随机因素,但不得超出规定范围。

区分禁止重试的错误

backoff.sh 区分禁止重试的错误。收到 HTTP 400/401/403/404/409 时立即停止,并以 attempts=1 结束。(使用 /bad 确认。)

格式错误发送 100 次也会失败 100 次。请在脚本中加入根据状态码判断是否重试的规则。

加载到 DLQ

创建 /root/r/senddlq.sh。接收两个参数(URL 메시지ID),重试超过最大次数时创建 /root/r/dlq/<메시지ID>.json。JSON 必须包含 msg_id, url, reason, attempts, first_failed_at 五个键。(url 是第 7 步重新处理时再次调用的目标。)使用 /dead 和消息 ID M-001 运行并创建文件。(使用同一 ID 再次运行时覆盖同一文件。)

如果 DLQ 中只保存原始消息,日后人工无法作出判断。必须同时保存原因、尝试次数和首次失败时间。

DLQ 重新处理脚本

创建 /root/r/dlq-replay.sh。接收一个参数(DLQ 目录),对其中每个 .json 尝试重新处理;失败时将该文件的 attempts 增加 1 后重新保存。最后一行输出 replayed=<시도수> succeeded=<성공수>。目录不存在时以非 0 退出码结束。

重新处理必须通过参数接收目标目录,才能保证安全。如果不保留重新处理历史,就无法知道已经尝试了多少次。

重试策略表

创建 /root/r/policy.csv。第一行为 error,retry,max_attempts,backoff,final。必须包含以下六种类型:connection-refused, timeout, http-500, http-400, http-401, http-429retryY/N/조건부finalDLQ/중단/통보 之一。

不同错误类型的重试判断和最大次数不同。尤其需要注意 429,因为它表示对方要求“放慢请求速度”。