现场是这样的。上游 429 返回 Retry-After: 0,客户端按返回值 sleep 后立刻重试,一秒发了 200 次,十分钟后账号被升级成封禁。

中间走过弯路,把「缺少最小退避时间」当成主因,改完现象没变。

原因不止一个,得分开看。把 0 当成可以立即重试;没有设置最大重试次数;并发请求共用同一个重试计数器。

给退避设一个下限

python -c "print('delay = max(1.0, float(retry_after))')"

叠加指数退避和随机抖动

python -c "print('delay = min(cap, base * 2 ** n) * random.uniform(0.5, 1.5)')"

接着设置最大重试次数和总耗时上限

python -c "print('max_retries=5, deadline=90')"

等前面都确认过,再按 key 维度统计重试次数观察放大

python retry_report.py --group-by api_key --window 10m

改之前我把相关日志按时间截了一段存着,事后对时间线用得上。

怎么算真的好了,看的是同一批现象。429 之后的重试间隔不小于 1 秒,单 key 每分钟请求数回到配额内,封禁解除后不再复现。

提醒一点。Retry-After 只是建议值,服务端返回 0 表示可以马上再试,不代表应该马上再试,客户端必须自己兜底。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189