当 Claude API 在高并发下开始返回 429,很多系统不是“慢一点”,而是直接雪崩:队列堆积、重试风暴、上游超时、下游告警连锁。

这篇给你一套可落地的生产方案:自适应并发 + 指数退避抖动 + 配额隔离。目标不是“永不 429”,而是把 429 控制在可恢复区间,并维持服务 SLO。

先定目标:别追求零错误,追求稳定恢复

建议先定 3 个可观测目标:

  • 429 比例:< 2%(5 分钟滑窗)
  • P95 总延迟:< 8s(含排队)
  • 重试放大量:< 1.3x(每个原始请求平均总尝试次数)

只要这 3 个指标稳住,业务层通常不会感知“灾难级抖动”。

一、把并发阀门从“写死”改成“自适应”

为什么固定并发会害死人

固定并发(比如永远 64)在流量高峰和模型配额波动时,容易出现“过载后还在猛踩油门”。

实现思路(AIMD)

  • 成功窗口内小步增加并发(Additive Increase)
  • 触发 429/5xx 时快速减小并发(Multiplicative Decrease)

伪代码:

type Gate struct {
    max int64 // atomic
}

func (g *Gate) OnHealthyWindow() {
    cur := atomic.LoadInt64(&g.max)
    atomic.StoreInt64(&g.max, min(cur+1, 128))
}

func (g *Gate) OnThrottle() {
    cur := atomic.LoadInt64(&g.max)
    next := int64(float64(cur) * 0.7)
    if next < 4 { next = 4 }
    atomic.StoreInt64(&g.max, next)
}

生产建议:

  • 上限别超过你实测稳定吞吐的 1.2x
  • 下限至少保留 4~8,避免完全“饿死”
  • 每 30s~60s 才允许加一次,避免抖动

二、重试必须“有脑子”:指数退避 + 全抖动

429 后立刻重试是最常见事故放大器。

正确姿势

  • 首次重试基线:200~400ms
  • 退避:base * 2^attempt
  • 抖动:rand(0, backoff)(Full Jitter)
  • 最大重试:在线请求建议 2~3

Go 示例:

func backoff(attempt int, base, capMs int) time.Duration {
    max := base * (1 << attempt)
    if max > capMs { max = capMs }
    return time.Duration(rand.Intn(max+1)) * time.Millisecond
}

关键:重试预算要和请求超时预算联动。总预算 8 秒,就别让第 3 次重试在第 9 秒才发出去。

三、配额隔离:别让一个租户拖垮全局

如果你做多租户,最危险的是“共享一锅并发和令牌”。

最低可用隔离

  • 全局池:保护上游配额
  • 租户池:限制单租户爆发
  • 优先级池:交互请求 > 批处理请求

一个简单配比:

  • 全局并发 60
  • 关键租户保底 20
  • 普通租户共享 40
  • 批处理最多吃 15(且可被抢占)

这样即便批处理任务打满,也不会把在线流量一起带走。

四、熔断 + 半开恢复:阻止重试风暴二次伤害

在 429/5xx 连续高于阈值时,短路一段时间:

  • Open 条件:最近 30s 错误率 > 25%
  • Open 时长:10~20s
  • Half-open:只放 5~10% 探针流量
  • 探针恢复成功再逐步放量

配合“降级响应”(如返回缓存摘要、延迟提示)比一股脑超时好太多。

五、观测面板:没有这几张图,你是在盲飞

至少补齐:

  • requests_total{model,status}
  • retry_attempts_histogram
  • queue_wait_ms_p95
  • inflight_by_tenant
  • circuit_breaker_state

排障顺序:

  1. 先看 429 是否集中在某模型/某租户
  2. 再看队列等待是否先抬升
  3. 最后看重试放大量是否失控

六、可直接落地的“止血配置”

适合先救火:

llm_gateway:
  timeout_ms: 8000
  max_inflight_global: 48
  max_inflight_per_tenant: 12
  retry:
    max_attempts: 3
    base_backoff_ms: 250
    cap_backoff_ms: 2500
    jitter: full
  circuit_breaker:
    error_rate_threshold: 0.25
    open_seconds: 15
    half_open_probe_ratio: 0.1

先用这套把系统拉回稳态,再按真实流量做细化调参。

常见误区

  • 误区 1:把 429 当网络抖动处理 → 结果是无限重试
  • 误区 2:所有请求同优先级 → 关键链路被批任务淹没
  • 误区 3:只有全局限流没有租户隔离 → 大客户把小客户全拖死

总结

Claude API 的 429 不可怕,可怕的是没有“系统级节流策略”。

用这 3 件事就能显著降风险:

  1. 自适应并发(动态阀门)
  2. 指数退避 + 全抖动(抑制重试风暴)
  3. 配额隔离 + 熔断恢复(保护关键流量)

先把系统做成“可退化、可恢复、可观测”,再追求峰值吞吐。稳定赚钱,比偶尔跑得快更重要。