日期:2026-09-08
我们网关后面挂了多家模型供应商,有的用开源社区模型、有的用商业 API,能力参差不齐。上线初期最头疼的不是模型效果,是错误处理。各家报错格式五花八门:有的抛标准 JSON,有的返回一整页 HTML 错误页,还有的更绝,把错误塞进 HTTP 200 的正常响应体里,业务侧解析到一半才发现有问题。
调用方苦不堪言。每接一家供应商,就得写一套错误解析逻辑,联调周期被拉得老长。出了问题排查更是各说各话,供应商说"我返回了错误码",业务方说"我收到的是 200 啊",对着同一段日志能吵半小时。我们意识到,网关最被低估的价值,是替下游把这些脏活收拾干净。
我们做的第一件事是统一错误码体系。定义一套覆盖全链路的错误码(参数非法、鉴权失败、供应商超时、内容风险、配额超限等),每个码有稳定含义和文档,调用方只认这一套。异常拦截与归一是第二段,在网关出口处统一捕获下游异常,无论供应商返回什么形态,都翻译成我们的标准错误结构和 HTTP 状态码,不让原始脏数据漏给调用方。结构化错误响应放第三段,错误体统一字段(code、message、trace_id、retryable),调用方一次适配处处可用。错误率监控与告警兜底,按错误码维度统计分布,某类错误突增立刻告警,定位问题不用再翻各家供应商文档。
案例片段(已脱敏): 统一错误码与异常归一配置片段(示意):
error_code: INVALID_PARAM: 400 AUTH_FAIL: 401 UPSTREAM_TIMEOUT: 504 CONTENT_RISK: 422 normalize: catch_all: true body: {code, message, trace_id, retryable} map_html_error: true上线后调用方适配成本下降约 70%,错误定位平均时效从约 40 分钟降到约 5 分钟,异常归一覆盖率约 98%,排障工时约减半(数据均为脱敏示意值)。
第一个坑是"200 里藏错误"。最阴间的供应商把错误当成功返回,业务解析正常响应结构却拿不到预期字段。我们在网关层加了响应结构校验,凡是声明成功但结构不符、或带错误特征字段的,统一拦截并归一为内部错误,不把它当正常结果放行。这一步是归一能不能彻底的关键。
第二个难点是向后兼容。统一错误码一动,老调用方就崩。我们给错误码做了版本前缀和弃用期,新增码不破坏旧语义,废弃码留过渡窗口,调用方有充足时间迁移。这里有个权衡,规范太严老系统吃不消、太松又统一不起来,我们按"新增从严、存量从宽"的原则推进。
第三个点是错误语义的一致性。同一个"超时",有的供应商叫 timeout 有的叫 deadline_exceeded,映射到我们的码必须唯一且含义稳定,否则监控按码统计时会分裂。我们建了一张完整的映射表并审了一遍,确保每种下游异常有且只有一个归宿。
调用方适配成本下降约 70%,错误定位平均时效从约 40 分钟降到约 5 分钟,异常归一覆盖率约 98%,排障工时约减半。新供应商接入从"写一套解析"变成"对着文档填映射",联调周期明显缩短。我们复盘时发现,错误率监控意外成了稳定性抓手,某次一家供应商悄悄把超时率从 1% 拉到 8%,告警比业务投诉早了半天发现。
我们还把错误码分布做成了供应商健康度评分,哪家爱超时、哪家爱报错,量化出来后议价和切换都有了依据,网关从纯通道变成了治理节点。
我们后来把错误码分布做成了供应商健康度评分,哪家爱超时、哪家爱报错、哪家爱把错误藏进 200,量化出来后议价和切换都有了依据,网关从纯通道变成了治理节点,话语权也上来了。还有一块是重试语义,归一之后我们明确了哪些错误码可重试、哪些不可,调用方拿到 retryable 字段直接决策,不再自己猜,重试风暴这一类事故少了很多。错误监控接进值班告警后,某次一家供应商悄悄把超时率从 1% 拉到 8%,告警比业务投诉早了半天发现,我们及时切了备用供应商,用户侧几乎无感,这才真正体现出统一错误治理的价值,不只是联调痛快一点。
顺便记一个边界情况,是供应商返回的畸形 JSON。有次某家把字段名大小写混用,我们的归一层按固定字段解析直接报错,反而把正常错误吞了,排查时以为是网关自己挂了。后来归一层做了字段容错和标准化,先归一结构再映射错误码,畸形响应也能稳定翻译成内部码。这种脏数据永远会有,网关的归一逻辑必须比下游更皮实,不能指望上游都规规矩矩。
网关的价值之一是替下游收拾脏活,错误不归一,每个调用方都在重复造轮子,联调成本全堆在接入期,这事我们接手前没人管。200 里藏错误最阴,网关层必须做响应结构校验,不能迷信 HTTP 状态码。我后来觉得,错误码映射表值得花半天仔细审,语义分裂会直接毁掉后面的监控,看似琐碎其实是地基。向后兼容别偷懒,规范一动老系统就崩,弃用期留够大家都轻松。说到底,统一错误码不是技术洁癖,是让整条链路从"各说各话"变成"同一种语言"。