先把当时的情况摆出来。上游返回 429 但响应头里没有 Retry-After,客户端按固定一秒重试,连续失败十几次后任务被判定超时,运维只看到一条超时告警。

把日志和配置对完,能解释这个现象的基本就这几处。网关层限流不会附带建议等待时间。另一种情况是重试次数和间隔都没有上限,耗尽时间预算。

不容易想到的是,多实例同时重试形成同步风暴。还有一种情况,把执行超时设得比总重试时间还短,永远等不到成功。

第一件事是读响应头,有就用没有就自己算

wait = int(resp.headers.get('Retry-After', 0)) or min(2 ** n, 30)

接着在退避里加随机抖动打散重试时间

wait += random.uniform(0, wait * 0.3)

然后设置总等待上限,超过就降级或入队

if total_wait > 120: enqueue(task)

剩下的就是把重试次数和等待时长打进监控

print({'n': n, 'wait': wait})

恢复的判定其实很直接。任务整体成功率恢复到百分之九十九以上,单个请求的平均重试次数降到两次以内,超时告警也不再出现。

容易忽略的是,Retry-After 的值可能是秒数也可能是 HTTP 日期,直接 int 转换会抛异常,解析前要先判断格式再计算等待时间。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189