MonkeyAI 排错 · HTTP 504

API 504 Gateway Timeout:怎么判断是哪一层超时

504 不代表 API Key 错了,也不能直接推断模型服务商宕机。它只说明某个网关在等待上游响应时超时。排查时先把请求缩到最小,并保留请求 ID、UTC 时间和耗时。

HTTP 504 能说明什么?

RFC 9110 对 504 Gateway Timeout 的定义很窄:作为网关或代理的服务器,没有及时收到完成请求所需的上游响应。这个状态码说明“等待超时”发生在网关层,但没有指出到底是哪一段变慢。

看到 504 时,不能直接写成“模型服务商挂了”。延迟可能出现在中转服务、内部路由、网络链路或模型服务商。需要结合日志和请求信息判断。
本文的验证范围(2026 年 8 月 3 日):我们实测了线上模型列表的认证响应,核对了生产服务日志,并逐项对照 RFC 9110、Nginx 和 OpenAI 官方文档。我们没有故意拖住生产请求来制造 504。下面是超时诊断方法,不代表 MonkeyAI 当前正在发生 504。
响应可以确认的事实下一步
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 与私密内容
不要把完整 API Key 粘贴到工单或截图里。请替换成 ***,prompt 里的私密内容也要脱敏。

为什么流式请求也会 504?

流式响应可以让客户端边接收边处理,但不会消除代理超时。Nginx 对 proxy_read_timeout 的定义是“上游两次读取之间允许等待的时间”,不是整段响应的总时长;文档中的默认值是 60 秒。

如果上游在这段时间里没有发出任何数据,代理仍可能关闭连接。流已经向客户端发送部分内容后,代理也不能假装请求从未发生并安全重放整段响应。

对于确定会长时间运行的任务,只有在目标接口和客户端明确支持时,才使用异步或 background 模式。OpenAI 为 Responses API 的长任务记录了 background: true;它不是所有 OpenAI 兼容接口的通用能力。

生产日志锚定案例(2026 年 8 月 7–8 日)

以下数据来自网关主机上真实的 journalctl -u sub2api --since '24 hours ago' 输出,不是为了演示而人为触发。数字反映的是一个压测模型 gpt-5.6-sol 反复打到同一条路径上。结论不是"网关坏了",而是"如何在日志里区分上游 502 与网关无账号可用的 503"。

状态码24 小时出现次数路径延迟信号日志告诉你的事
50247/v1/chat/completions170–570 ms上游返回 502;网关原样透出(带 account_id 16)
503235/v1/chat/completions13–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 一定来自"已经没有可重试机会"的那一层。
本节证据不能证明什么。这个 24 小时窗口里没有出现 Nginx 意义上的 504;只有 Go 网关返回的 502/503。如果客户端真的收到了 504,问题是这一层网关之上的代理——本指南的诊断步骤同样适用,但"是哪个计时器先到"的答案会指向不同的层。

有限重试,避免重复任务

  1. 响应里有 Retry-After 时,按它指定的时间等待。
  2. 没有该响应头时,短暂等待后使用带随机抖动的指数退避。
  3. 只有在允许重复生成,或已有幂等方案时,才重试失败的生成请求。
  4. 尝试少量次数后停止,把上面的请求证据交给支持人员。

OpenAI 的公开错误文档对临时 503 提供了重试建议,限流文档说明了 Retry-After 和退避策略;但官方文档没有承诺 504 一定携带 Retry-After。

添加重试循环前,请先检查客户端。OpenAI 官方 Python 库默认会对连接错误、408、409、429 和 HTTP 5xx 响应重试两次,504 也在其中。应用层再套一层循环,可能让一次可见操作变成多次上游请求。受控排障时,如需只观察一次请求,可暂时设置 max_retries=0;排障结束后应恢复明确的重试策略。

服务端运维检查

如果你负责中转服务,请用客户端的 UTC 时间对齐反向代理和应用日志。重点查看上游地址、连接耗时、首字节时间、响应状态,以及连接关闭前是否已经向客户端发送数据。

Nginx 配置文档定义
proxy_connect_timeout等待建立上游连接的时限;默认 60 秒
proxy_read_timeout上游两次读取之间的时限;默认 60 秒
proxy_send_timeout向上游连续两次写入之间的时限;默认 60 秒
proxy_next_upstream默认重试条件不包含上游返回的 HTTP 504
Sub2API 运维参考:上游项目当前的边缘安全指南为长连接 SSE 与 WebSocket 提供了一份 Nginx 基线,其中关闭 proxy_buffering 和 proxy_request_buffering,并将读取、发送超时设为 1800 秒。这只适合拿来对照,不是生产处方。改动前还要核对实际故障路由、CDN 限制和自己的延迟数据。

不要一上来就把所有超时全局调大。先确认触发的是哪个计时器、哪条路由确实需要更长时间,再改生产配置。

资料来源

常见问题

504 是否说明 API Key 无效?

不是。认证无效通常返回 401;504 表示网关等待上游响应时超时。

/v1/models 成功,是否代表对话接口一定可用?

不代表。它只能确认 Key 和 Base URL 对模型列表接口有效,不能保证某个推理接口或模型健康。

504 后应该一直重试吗?

不应该。短暂等待后重试一次;允许重复任务时再使用有限退避。连续失败后停止,并保留请求证据。

stream=true 能避免 504 吗?

不能。流式响应可以缩短用户感知的等待时间,但上游仍需在代理读取超时前发送数据。

运维人员应该全局调大 proxy_read_timeout 吗?

不建议。先确认哪个计时器触发、哪条路由需要更长时间。全局调大可能掩盖慢上游或故障。

相关教程

API 502 Bad Gateway 排错

相反的失败模式:上游确实回复了,但回复无效。用延迟与日志形态来区分 502 和 504。

打开页面

API Key 排错

先区分 401、404 和模型错误。

打开页面

Base URL /v1

检查漏写或重复的 API 路径。

打开页面

SSE 流式输出

排查缓冲、断流和不同接口的事件格式。

打开页面

429 限流排错

识别限流信号,并避免重试形成请求风暴。

打开页面

联系支持

提交脱敏后的时间和请求证据。

打开页面