API 504 Gateway Timeout:怎么判断是哪一层超时
504 不代表 API Key 错了,也不能直接推断模型服务商宕机。它只说明某个网关在等待上游响应时超时。排查时先把请求缩到最小,并保留请求 ID、UTC 时间和耗时。
HTTP 504 能说明什么?
RFC 9110 对 504 Gateway Timeout 的定义很窄:作为网关或代理的服务器,没有及时收到完成请求所需的上游响应。这个状态码说明“等待超时”发生在网关层,但没有指出到底是哪一段变慢。
| 响应 | 可以确认的事实 | 下一步 |
|---|---|---|
| 401 JSON | 接收请求的服务拒绝了认证 | 检查 Bearer 头、Key、账号或 IP 限制 |
| 404 | 路由或资源不存在 | 检查 Base URL、接口路径和 Model ID |
| 429 | 触发了已定义的速率或额度限制 | 查看错误码、限流头和 Retry-After |
| 504 | 网关等待上游时超时 | 保留请求 ID、耗时、路由、stream 状态和服务端日志 |
先做一个最小验证
重复发起耗时生成前,先单独验证认证。模型列表接口不会要求推理模型生成内容,适合把 Key 问题与生成超时拆开。
: "${MONKEYAI_API_KEY:?请先设置 MONKEYAI_API_KEY}"
curl -i https://monkeytheme.com/v1/models \
-H "Authorization: Bearer $MONKEYAI_API_KEY"返回 200 和 JSON 模型列表,说明该 Key 可以访问模型列表接口;这不保证每个模型和推理接口都处于健康状态。返回 401 表示认证失败,不是上游超时。
接着保持模型和接口不变,把 prompt 缩短、输出上限调小,只重试一次。如果小请求成功,请保留两次请求的耗时。这个对比有助于定位,但不能单凭它断定原请求“太大”。
用一条命令保存状态、响应头和耗时
下面的命令把响应头和正文分别保存到临时目录,同时输出连接耗时、首字节耗时和总耗时。与只截一张“504”报错图相比,这些数据更利于支持人员定位问题。
: "${MONKEYAI_API_KEY:?请先设置 MONKEYAI_API_KEY}"
workdir=$(mktemp -d)
curl -sS \
-D "$workdir/headers.txt" \
-o "$workdir/body.txt" \
-w 'status=%{http_code}\nconnect=%{time_connect}s\nfirst_byte=%{time_starttransfer}s\ntotal=%{time_total}s\n' \
https://monkeytheme.com/v1/models \
-H "Authorization: Bearer $MONKEYAI_API_KEY"
printf '文件保存在 %s\n' "$workdir"如果 body.txt 是 JSON,请在脱敏后保留错误类型和消息;如果它是 HTML 网关错误页,也应注明。HTML 504 与结构化 JSON API 错误可能来自不同层。分享 headers.txt 前,应先删除 Cookie、追踪信息和其他敏感字段。
重试前先保留这些信息
| 记录项 | 用途 |
|---|---|
| UTC 时间与总耗时 | 便于把客户端报错与代理、上游日志对应起来 |
| 接口路径和 Model ID | 定位路由,不必暴露 prompt |
| HTTP 状态和响应正文 | 区分网关 HTML 错误页与结构化 API 错误 |
| Request ID / trace header | 如果响应里有,这是查日志最有效的索引 |
stream 设置 | 判断超时发生在首个事件前还是两个事件之间 |
| 一份脱敏后的请求结构 | 核对 token 和输出设置,同时保护 Key 与私密内容 |
***,prompt 里的私密内容也要脱敏。为什么流式请求也会 504?
流式响应可以让客户端边接收边处理,但不会消除代理超时。Nginx 对 proxy_read_timeout 的定义是“上游两次读取之间允许等待的时间”,不是整段响应的总时长;文档中的默认值是 60 秒。
如果上游在这段时间里没有发出任何数据,代理仍可能关闭连接。流已经向客户端发送部分内容后,代理也不能假装请求从未发生并安全重放整段响应。
background: true;它不是所有 OpenAI 兼容接口的通用能力。生产日志锚定案例(2026 年 8 月 7–8 日)
以下数据来自网关主机上真实的 journalctl -u sub2api --since '24 hours ago' 输出,不是为了演示而人为触发。数字反映的是一个压测模型 gpt-5.6-sol 反复打到同一条路径上。结论不是"网关坏了",而是"如何在日志里区分上游 502 与网关无账号可用的 503"。
| 状态码 | 24 小时出现次数 | 路径 | 延迟信号 | 日志告诉你的事 |
|---|---|---|---|---|
| 502 | 47 | /v1/chat/completions | 170–570 ms | 上游返回 502;网关原样透出(带 account_id 16) |
| 503 | 235 | /v1/chat/completions | 13–25 ms | 没有可用账号——根本未联系上游就快速失败 |
| 200 | (其余 1457 次中的多数) | 同 | 不等 | 正常路径 |
同一时间窗口里三类错误指纹(已脱敏到内部模型 gpt-5.6-sol 与账号 16):
# (1) 上游返回 401 → 网关切换账号(max_switches 用尽后对外返 502)
... openai_chat_completions.upstream_failover_switching {
"model": "gpt-5.6-sol", "stream": true,
"account_id": 16, "upstream_status": 401,
"switch_count": 1, "max_switches": 10
}
# (2) 上游切账号耗尽后,最终对客户端返回 502
... http request completed {
"path": "/v1/chat/completions", "method": "POST",
"status_code": 502, "latency_ms": 570,
"account_id": 16, "platform": "openai", "model": "gpt-5.6-sol"
}
# (3) 更快的失败路径:网关根本没有可用账号 → 13–25 ms 内返 503
... openai_chat_completions.account_select_failed {
"model": "gpt-5.6-sol", "stream": true,
"error": "no available accounts", "excluded_account_count": 0
}
... http request completed {
"status_code": 503, "latency_ms": 15
}
- latency_ms 13–25 + 503 + "no available accounts"——网关根本没联系到上游。这是运维侧问题(账号池耗尽、全部在冷却、或者模型未挂载),不是网络超时。给客户端加重试没有意义。
- latency_ms 170–570 + 502 +
upstream_failover_switching——上游有响应,但状态码不对。网关最多切到max_switches个账号后才把 502 透出来。这是典型的"代理健康、上游账号池病"的模式。 - 这两条日志都不是 Nginx 意义上的 504。如果客户端日志里出现了真正的 504,意味着这一层网关之上的另一层代理(通常是 CDN 或边缘代理)自己的读超时到期了——也就是另一个计时器先到。验证方法:看那层代理的
access.log里的 upstream 状态码与延迟;504 一定来自"已经没有可重试机会"的那一层。
有限重试,避免重复任务
- 响应里有
Retry-After时,按它指定的时间等待。 - 没有该响应头时,短暂等待后使用带随机抖动的指数退避。
- 只有在允许重复生成,或已有幂等方案时,才重试失败的生成请求。
- 尝试少量次数后停止,把上面的请求证据交给支持人员。
OpenAI 的公开错误文档对临时 503 提供了重试建议,限流文档说明了 Retry-After 和退避策略;但官方文档没有承诺 504 一定携带 Retry-After。
max_retries=0;排障结束后应恢复明确的重试策略。服务端运维检查
如果你负责中转服务,请用客户端的 UTC 时间对齐反向代理和应用日志。重点查看上游地址、连接耗时、首字节时间、响应状态,以及连接关闭前是否已经向客户端发送数据。
| Nginx 配置 | 文档定义 |
|---|---|
proxy_connect_timeout | 等待建立上游连接的时限;默认 60 秒 |
proxy_read_timeout | 上游两次读取之间的时限;默认 60 秒 |
proxy_send_timeout | 向上游连续两次写入之间的时限;默认 60 秒 |
proxy_next_upstream | 默认重试条件不包含上游返回的 HTTP 504 |
proxy_buffering 和 proxy_request_buffering,并将读取、发送超时设为 1800 秒。这只适合拿来对照,不是生产处方。改动前还要核对实际故障路由、CDN 限制和自己的延迟数据。不要一上来就把所有超时全局调大。先确认触发的是哪个计时器、哪条路由确实需要更长时间,再改生产配置。
资料来源
常见问题
504 是否说明 API Key 无效?
不是。认证无效通常返回 401;504 表示网关等待上游响应时超时。
/v1/models 成功,是否代表对话接口一定可用?
不代表。它只能确认 Key 和 Base URL 对模型列表接口有效,不能保证某个推理接口或模型健康。
504 后应该一直重试吗?
不应该。短暂等待后重试一次;允许重复任务时再使用有限退避。连续失败后停止,并保留请求证据。
stream=true 能避免 504 吗?
不能。流式响应可以缩短用户感知的等待时间,但上游仍需在代理读取超时前发送数据。
运维人员应该全局调大 proxy_read_timeout 吗?
不建议。先确认哪个计时器触发、哪条路由需要更长时间。全局调大可能掩盖慢上游或故障。