图 1:本文封面,三个错误码都挂在网关与上游之间这一段链路上。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。「50X 错误码的内涵」是网络面试的常见题,背出「服务器内部错误」只能拿一半分——面试官想听的是:在「客户端—网关—上游」这条链路上,每个码各自把锅扣在谁头上。
想象面试现场:「用户报错说接口 502 了,你怎么定位?502、503、504 有什么区别?」
先立坐标系:这三个码都发生在网关与上游之间。
现代架构里请求很少直连应用服务器,中间隔着反向代理或网关:Nginx、API 网关、负载均衡器。502、503、504 的共同点,是它们都是「网关这个角色」替上游发的言;区别在上游到底出了什么状况。RFC 9110 对三者各有精确定义,我们逐个对照着看。
502 Bad Gateway:上游的话,网关听不懂或者根本没等到。
RFC 9110 的定义:服务器作为网关或代理时,从其访问的上游服务器收到了无效响应。「无效响应」在实践里主要是两类:一是根本连不上——上游进程崩溃、端口不通、连接被拒;二是连上了但回的东西不成样子——协议不匹配、返回体损坏。责任方在上游进程或它所在的网络。排查方向:上游服务还活着吗?端口在监听吗?网关到上游的网络通吗?
503 Service Unavailable:上游活着,但主动说不。
RFC 9110 的定义:服务器因临时过载或计划维护,当前无法处理请求,预计过一阵会缓解;并且明确可以带一个 Retry-After 头部,建议客户端等多久再试。关键词是「主动」和「临时」——上游不是死了,是开启了自我保护:过载限流、熔断拒收、滚动维护。责任方在容量。排查方向:上游的 QPS 与资源水位是不是顶到了限流阈值?是不是正好在发布窗口?
504 Gateway Timeout:上游接了活,但没按时交卷。
RFC 9110 的定义:网关没有在时限内从上游收到完成请求所需的响应。与 502 的「连不上」不同,504 是「连上了、请求也接了,但处理超时」——慢查询、死锁、下游依赖卡住、推理超时,都是典型病因。责任方在上游性能。排查方向:上游的慢日志、线程池是不是打满、它自己的下游依赖是不是卡住了。
一段代码把三种形态跑一遍。
下面这份代码模拟网关调用上游,把四种故障形态映射到不同状态码。用 Node.js 22.15.0 直接运行(node gateway.js)即可:
'use strict';
// 模拟网关调用上游:按上游的故障形态映射状态码
function gatewayCall(upstream) {
let response;
try {
response = upstream();
} catch (e) {
if (e.code === 'ECONNREFUSED') {
return { status: 502, note: '连不上上游:进程崩溃或端口不通' };
}
throw e;
}
if (response.invalid) {
return { status: 502, note: '上游回了无效响应:协议错误、返回体损坏' };
}
if (response.refused) {
return { status: 503, note: '上游主动拒收:过载保护或维护中', retryAfter: response.retryAfter };
}
if (response.elapsedMs > response.deadlineMs) {
return { status: 504, note: '上游接了请求但没在时限内答完:慢查询或推理超时' };
}
return { status: 200, note: '正常' };
}
const scenarios = {
上游进程崩溃: () => { const e = new Error('connect refused'); e.code = 'ECONNREFUSED'; throw e; },
上游返回损坏报文: () => ({ invalid: true }),
上游过载限流: () => ({ refused: true, retryAfter: 30 }),
上游推理超时: () => ({ elapsedMs: 3200, deadlineMs: 3000 }),
上游正常: () => ({ elapsedMs: 120, deadlineMs: 3000 }),
};
for (const [name, upstream] of Object.entries(scenarios)) {
const r = gatewayCall(upstream);
const extra = r.retryAfter ? `(Retry-After: ${r.retryAfter}s)` : '';
console.log(`${name} -> ${r.status}${extra}:${r.note}`);
}
实测输出:
上游进程崩溃 -> 502:连不上上游:进程崩溃或端口不通
上游返回损坏报文 -> 502:上游回了无效响应:协议错误、返回体损坏
上游过载限流 -> 503(Retry-After: 30s):上游主动拒收:过载保护或维护中
上游推理超时 -> 504:上游接了请求但没在时限内答完:慢查询或推理超时
上游正常 -> 200:正常
图 2:三码语义对照与「活没活、忙不忙、慢不慢」的定位路径。
定位口诀与易错点。
口诀一句话:先看上游活没活(502),活着再看忙不忙(503),不忙再看慢不慢(504)。工程落地上再加两步:先看网关日志里的 upstream 状态与耗时,把「网关自身故障」和「上游故障」分开;再按码定责任方,502 找进程与网络,503 找容量与限流配置,504 找性能与依赖。
易错点有三。一是把 504 当成网关自己坏了——网关只是计时员,超时发生在上游。二是混淆 500 与 502:500 是上游应用自己处理失败、明明白白回了个错误;502 是网关认为上游的响应根本无效,或者压根没收到响应。三是重试不分码:503 带 Retry-After 就按它说的等;504 之后重试要想清楚上游那笔慢操作是否已经生效(写操作超时重试可能重复执行);502 通常可以安全重试,但先确认上游没有处于半死不活的抖动期。
这套定位法在 AI 应用里几乎天天用:调用模型 API 报错,502 指向服务商网关与推理集群之间的连接问题,503 是上游容量打满在限流,504 是这次推理太慢超时——告警分组和重试策略都该按这个分。你在生产上被 50X 半夜叫醒过吗?最后定位到哪一层?欢迎分享你的排查故事。