限流与 429 是什么
服务端为保护自身与公平分配资源,会按维度限制请求速率:常见的是每分钟请求数、每分钟 token 数与并发数。超限时返回 429,响应里通常带有限流维度与剩余量的头部信息,可用于动态调整。429 与余额耗尽、权限不足是不同的性质:前者等待或降速即可恢复,后者需要充值或改配置,混在一起处理会掩盖真正的问题。
基本信息
| 状态码 | 429,含义是请求超过当前速率或配额限制 |
|---|---|
| 速率维度 | 每分钟请求数、每分钟 token 数、每日配额、并发数 |
| 响应头部 | 常带限流上限、剩余量与重置时间信息 |
| 区别于余额 | 余额不足类错误需充值或提额,与降速无关 |
| 处理原则 | 退避重试加削峰,并区分可重试与不可重试 |
| 监控 | 按维度统计触发次数,用于容量规划 |
限流维度对照
| 维度 | 含义 | 典型触发场景 |
|---|---|---|
| 每分钟请求数 | 单位时间内允许的请求条数 | 突发并发或循环调用密集时最先触发 |
| 每分钟 token 数 | 单位时间内允许消耗的 token 量 | 长上下文与大输出请求更容易触发 |
| 每日配额 | 一天内的总量上限 | 批量任务连续跑数小时后触发 |
| 并发数 | 同时进行中的请求数量 | 流式长任务占用连接较久时触发 |
| 账户额度 | 余额或套餐额度耗尽 | 与速率无关,需充值或提额 |
典型使用场景
- 批量任务:控制并发与速率,避免长时间触发限流
- 多租户系统:按租户分配配额,防止单租户挤占全部份额
- 峰值削峰:短时高峰把请求排队,按速率平稳发出
- 降级策略:限流时先降模型档位或缩短输出,保证核心功能可用
- 容量规划:按触发频率判断是否需要提额或优化调用方式
- 定时任务错峰:把日报与批处理挪到业务低峰时段,与在线流量互不挤压
退避与限速的基本写法
退避与限速的基本写法
import time, random
for i in range(5):
r = call_api()
if r.status_code != 429:
break
wait = header_retry_after(r) or (2 ** i) # 优先用服务端给的重置时间
time.sleep(wait + random.random() * 0.5) # 叠加抖动,避免同时重试
else:
enqueue_for_later() # 连续失败则转入延迟队列
# 出站侧再加一层令牌桶限速,把并发压在额度以内,从源头减少 429常见处理手段
| 手段 | 做法 | 适用 |
|---|---|---|
| 指数退避加抖动 | 按次倍增等待并叠加随机量 | 偶发限流的普遍场景 |
| 客户端限速 | 用令牌桶或信号量控制出站速率 | 自建服务与批处理任务 |
| 请求排队 | 高峰排队、低谷放量 | 报表与离线任务 |
| 分级降级 | 限流时切小模型或缩短输出 | 在线交互的兜底 |
| 提高配额 | 按监控数据向平台申请提额 | 业务量确实增长时 |
限流头部信息值得记录:留着它可以看清触发的是请求数还是 token 维度,也能据此校准自己的出站限速阈值。
批量任务不要用固定间隔死等:按服务端给出的重置时间动态调整,整体完成时间通常明显更短;把待处理任务持久化到队列里,进程重启后能接着跑,也能在限流缓解后自动加速。
触发 429 后立即并发重试是最糟的做法:请求量被放大,反而更容易持续超限。