网关里给所有下游调用套了统一重试,max_retries=3,日志里一条 400 invalid_request_error 被原样重发了三次,三次都失败,用量统计里这条请求计了四份,月底对账多出 7% 的调用量。
一开始怀疑是「重试时没换请求 id,服务端按同一次请求计费」,查了一圈,方向不对。
把日志和配置对完,能解释这个现象的基本就这几处。重试策略没有区分状态码,4xx 属于永久错误不该重试。也可能是SDK 自带重试和业务层重试叠加,实际次数是乘积。
少数情况下是超时判定的时间设得比上游处理时间短,成功也被当成失败。
第一件事是统计各类状态码的重试次数
python -c "import collections;print(collections.Counter({400:3,429:12,500:5}))"
然后把 4xx 排除在重试范围外
python -c "from tenacity import retry,retry_if_exception_type;print('retry only on Timeout and 5xx')"
随后关掉 SDK 内置重试避免叠加
python -c "import openai;c=openai.OpenAI(max_retries=0);print(c.max_retries)"
末了重试时带上幂等键便于对账
curl -s https://api.example.com/v1/orders -H 'Idempotency-Key: 20261111-8821' -d '{"sku":"A1"}' -o /dev/null -w '%{http_code}\n'
别只看报错消失,验证要多做一步。400 这类请求只发一次就返回错误,5xx 与超时仍然重试,调用量统计与业务请求数一一对应,多计的部分消失。
重试的前提是错误可恢复,把 4xx 一起重试除了烧配额没有任何收益,先按状态码分类再决定重试策略。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。
