首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >429 之后该等多久?批量调用模型时别让重试把队列越堆越长

429 之后该等多久?批量调用模型时别让重试把队列越堆越长

原创
作者头像
用户9006099
发布于 2026-11-09 11:01:59
发布于 2026-11-09 11:01:59
60
举报

一项每天跑完一批摘要的任务,原先串行执行,改成十个 worker 并发后开始出现 429。最直观的处理是失败就再发一次,但如果所有 worker 同时退避、同时醒来,下一轮请求仍会撞在一起。更糟的是,重试任务排在新任务前面,队列长度和等待时间一起增长,直到用户看到的不是“偶尔限流”,而是整批都变慢。

429 只是一个状态码,不足以决定重试策略。先保存响应时间、请求 ID、模型 ID、接口、状态、Retry-After、速率限制相关响应头,以及当时的并发数。不同服务对请求数、输入令牌、输出令牌、日额度或账户余额的限制不同;某些 429 表示用量上限,并不会因为等几秒就消失。解析错误正文时先脱敏,日志里不要写 API Key、完整请求头或用户原文。

先给重试设边界

把请求分为“可安全重复的读请求”“有外部副作用的工具任务”。前者可在允许的状态下重试;后者必须先检查业务动作是否已执行。对每个任务保留固定 ID、总截止时间和最大尝试次数,不要让一次调用无限延长队列。Retry-After 有效时优先按它等待;缺失或无效时才用指数退避加随机抖动,并设置上限。

下面的伪代码刻意不绑定任何 SDK,因为部分 SDK 已经自动重试。若你再包一层重试,实际次数可能相乘。先查 SDK 设置和网关日志,再决定由哪一层负责:

代码语言:javascript
复制
deadline = now + task_timeout
for attempt in 1..max_attempts:
    response = call_once(task_id)
    if success: return response
    if status not in retryable_statuses: stop_and_record(response)
    if task_has_side_effect: query_operation_status(task_id)
    delay = valid_retry_after(response) ?? bounded_backoff_with_jitter(attempt)
    if now + delay >= deadline: stop_and_record("deadline")
    wait(delay)

注意 retryable_statuses 不应简单写成“所有 4xx 和 5xx”。401、403 通常需要修改凭证或权限;400 通常需要修请求;有些 429 是消费上限。网络超时则不能断定服务端没有收到请求。业务任务只有在确认外部动作状态后才能重放。

用队列而不是 worker 数控制入口

给每个模型或接口设独立并发上限,并记录等待中的任务数量。开始时可以用很小的并发,观察 429 和端到端耗时,再逐步增加。若限流持续,先降低入口速率;无限增加 worker 只会制造更多同时退避的任务。把新请求、重试请求和人工补跑区分开,避免旧任务饿死新请求。

完成一次批任务后核对四个数字:任务总数、最终成功数、明确失败数、仍未知状态数。成功数不能通过“写出了多少文件”推断,因为一次任务可能覆盖同名文件,也可能在超时后完成。按任务 ID 对账,才能决定哪些需要补跑。

如果你通过 NexoToken 这样的中转站调用多种模型,要把“客户端重试”“中转层处理”“模型服务返回”分层观察。先查自己能看到的请求与账单记录,再按同一任务对比不同模型的限额表现。不要把一次 429 归因到某个上游,也不要把换模型当作通用限流解决方案:模型切换还会改变输出和费用,需要重新验收。本文没有 NexoToken 的并发上限或跨平台测速数据。

依据:OpenAI 速率限制指南、Claude API 速率限制文档。具体响应头和额度以正在使用的接口、账号和日期为准。本文为工程方法与伪代码,不是某个平台的故障复盘。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 先给重试设边界
  • 用队列而不是 worker 数控制入口
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档