遇到 429 后该等多久?解析 Retry-After 并检查剩余预算
收到 HTTP 429 时,Retry-After 可能是秒数,也可能是 HTTP 日期。先把提示转换成等待时长,再与本次操作剩余预算比较。服务端要求等 60 秒而调用方只剩 2 秒时,应停止本次重试或交给有独立预算的异步任务。
1. 用固定时间验证等待决策
保存为 retry_after_lab.py,用 Python 运行。例中约定非法或缺失头部使用本地退避,并为下一次请求保留两秒。这里的日期解析使用 Python 邮件日期解析器做容错,只接受带时区的结果。
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
import re
def retry_delay(header, received_at):
if header is None:
return None
value = header.strip(" \t")
if re.fullmatch(r"[0-9]+", value):
digits = value.lstrip("0")
return float(digits or "0") if len(digits) <= 12 else float("inf")
try:
target = parsedate_to_datetime(value)
except (ValueError, OverflowError):
return None
if target.tzinfo is None:
return None
return max(0.0, (target - received_at).total_seconds())
def plan_retry(header, received_at, remaining, backoff):
hint = retry_delay(header, received_at)
delay = max(backoff, hint) if hint is not None else backoff
return delay if delay + 2 <= remaining else None
now = datetime(2026, 11, 13, 12, tzinfo=timezone.utc)
cases = [
("seconds", "5", 5),
("date", "Fri, 13 Nov 2026 12:00:04 GMT", 4),
("past date", "Fri, 13 Nov 2026 11:59:00 GMT", 1),
("invalid", "-1", 1),
("missing", None, 1),
("over budget", "60", None),
]
for label, header, expected in cases:
delay = plan_retry(header, now, remaining=8, backoff=1)
assert delay == expected
print(f"{label}: {'stop' if delay is None else f'wait {delay:g}s'}")
seconds: wait 5s
date: wait 4s
past date: wait 1s
invalid: wait 1s
missing: wait 1s
over budget: stop
2. 把等待放回请求生命周期
RFC 6585 允许 429 附带这个头,RFC 9110 同时定义了 503 上的等待含义。收到日期形式时,用接收响应的墙钟换算等待时长。执行等待和衡量总预算时用单调时钟,以免系统校时改变截止时间。样本中的超大秒数转换为无穷大,在有限预算下会停止重试。
重试入口还需确认操作可重放。读请求可以按服务合同重试,写请求则需要按具体接口确认幂等保证,例如 130 的幂等键。多客户端同时退避时,可在服务端等待下限之外加入随机抖动,并在等待后重算剩余预算。记录状态码、解析结果和停止原因,能够分辨服务端要求延后与客户端预算耗尽。
小结
把 Retry-After 当作重试的等待下限。是否真的重试还取决于操作语义和剩余预算。
数据来源与检索时间
| 来源 | URL | 检索日期 | 对应内容 |
|---|---|---|---|
| RFC 9110 §10.2.3 | https://www.rfc-editor.org/rfc/rfc9110.html#section-10.2.3 | 2026-11-13 | Retry-After 的两种格式及 503 语义 |
| RFC 6585 §4 | https://www.rfc-editor.org/rfc/rfc6585.html#section-4 | 2026-11-13 | 429 可携带 Retry-After |
| Python email.utils | https://docs.python.org/3.11/library/email.utils.html#email.utils.parsedate_to_datetime | 2026-11-13 | 日期解析及无时区返回值 |
核验说明
- Windows、Python 3.11.9 固定接收时间,核对六种决策分支。示例只做计算,未执行网络请求或真实等待。