API 中转站错误码怎么统一:从 429、503 到自定义 code 的归一化封装实践
同一个限流错误,不同上游可能返回 429、503,或者藏在响应体里的自定义 code,重试逻辑因此失效。本文拆解 API 中转站错误码统一封装的分层设计、映射表维护、流式错误处理与计费口径,说明统一错误结构如何降低多上游接入的维护成本。
做过多上游接入的团队大多遇到过同一类麻烦:同样一句「请求太频繁」,有的上游返回 429,有的返回 503,还有的把错误塞在 HTTP 200 的响应体里,给一个自家定义的数字 code。业务代码里于是散落着十几套 if-else,每换一家上游就要动一次核心调用链路,改完还得重新测一遍重试和降级。
错误码不统一的代价不只是代码难看。它会让重试策略失效——本该退避的请求被立刻重发,不该重试的鉴权失败被反复打到上游,最终既浪费额度,也把 Key 推到限流边缘。对终端用户来说,表现就是「有时报错、有时卡住、有时扣了钱没结果」,而排查时只能靠翻原始日志。
统一封装的第一步是分层。把错误拆成传输层(连接超时、TLS 握手失败)、协议层(HTTP 状态码异常、响应体不是合法 JSON)、鉴权层(Key 无效、余额不足、权限不足)、模型层(模型不存在、上下文超限、内容被拦截)和配额层(限流、并发超限、日额度耗尽)。只有同一层内的错误,才应该共享同一套处理逻辑。
第二步是定义一套稳定的内部错误结构。建议至少包含这几个字段:归一化后的 code、错误类型 type、来源上游标识、是否可重试 retryable、建议等待时长 retry_after,以及贯穿全链路的 request_id。code 面向程序判断,一旦定下就长期不变;message 面向人阅读,可以随上游措辞调整。两者分离,才能既稳定又便于排查。
映射表是整个封装的核心资产。以 OpenAI 兼容语义为基线,把各上游的错误归到有限的枚举值上:401 统一为无效凭证,403 为权限不足,404 为资源不存在,429 为触发限流,500、502、503 归为上游不可用。遇到上游自定义的错误码,不要直接透传,而是落到语义最近的枚举值,把原始 code 与原始报文放进 detail 字段保留线索。
重试策略要基于归类结果,而不是原始状态码。限流和上游 5xx 可以重试,但要遵循指数退避并尊重 retry_after;鉴权失败、模型不存在、内容被拦截属于确定性错误,重试只会放大问题。另外要留意幂等性,带副作用的工具调用或写操作不宜无脑重放,必要时配合去重键。
流式响应是最容易被忽略的一块。SSE 连接建立之后中途断开,HTTP 状态码往往已经是 200,错误只能通过流内事件传递。统一封装需要约定一个流内错误事件格式,让客户端能区分「正常结束」和「中途失败」,进而决定是续传、重发,还是直接向用户报错。
错误处理还直接关系到计费口径。上游已经计费、但中转端判定为失败的请求,如果直接回滚余额,对账时会出现缺口;如果照常扣费,用户又会觉得不透明。比较稳妥的做法是按上游真实返回结算,把失败原因写进调用明细,让每一笔消耗都能追溯。快米兔的模型 API 中转采用按量计费,调用明细与错误记录相互对应,开发者核对消耗时不用再拼凑多份日志。
可观测性要建立在归一化的错误码之上。只有当错误被收进统一枚举,才能按上游、按模型、按 Key 三个维度统计错误率,才能判断「是某一家上游在抖,还是某个模型整体不可用」。否则日志里全是格式各异的原始报错,运维只能靠人肉翻。
对外文档和 SDK 同样受益。错误结构稳定之后,文档可以一次性讲清所有 code 的含义与处理建议,客户端只需要写一套兜底逻辑。新接入一家上游时,业务侧不必改代码,只是映射表多了一行,回归测试也多了一条用例。
落到具体实践,快米兔的模型 API 中转走 OpenAI 兼容路线,注册送 5 元测试金,按量计费,具体额度与规则以官方说明为准。兼容协议加上统一错误码,带来的直接好处是接入成本低:先把调用跑通,再根据 retryable 和 retry_after 补上重试队列,整个过程不需要为每家上游单独写适配层。
需要提醒的是,错误码统一封装不是一次性工作,而是一份需要持续维护的映射资产。上游改版、新增模型、调整限流规则,都会让某些映射悄悄失效,因此最好把映射表纳入版本管理,配合回归测试定期校验。判断一套封装做得好不好,标准其实很朴素:业务代码里还有没有直接比较原始状态码的地方,运维能不能一眼看出故障范围,用户能不能拿到一句人话。做到这三点,错误码才算真正统一了。
