當(dāng)你看到 504 Gateway Time-out,網(wǎng)關(guān)其實(shí)是在說:我去上游要數(shù)據(jù),等太久了

你在瀏覽器里遇到的 504 Gateway Time-out 報(bào)錯(cuò),來自 HTTP 協(xié)議的狀態(tài)碼體系。它表示:當(dāng)前為你服務(wù)的這一跳服務(wù)器處在網(wǎng)關(guān)或代理的位置,它把你的請(qǐng)求轉(zhuǎn)發(fā)到了上游應(yīng)用或上游網(wǎng)關(guān),但在約定的等待時(shí)間內(nèi)沒有等到上游返回,于是只好回一個(gè) 504 給客戶端。IETF 的最新版語(yǔ)義規(guī)范把它定義得非常清楚:當(dāng)服務(wù)器充當(dāng)網(wǎng)關(guān)或代理,卻未能在規(guī)定時(shí)間內(nèi)收到其必須訪問的上游服務(wù)器的響應(yīng),就應(yīng)該返回 504。這與 502 Bad Gateway 的差異在于:502 是網(wǎng)關(guān)從上游收到了一個(gè)不合法或錯(cuò)誤的 HTTP 響應(yīng),而 504 是干脆沒在超時(shí)前等到有效響應(yīng)。(IETF Datatracker, MDN Web Docs)

把請(qǐng)求的旅程鋪開看更容易理解:瀏覽器 → CDN/WAF → 負(fù)載均衡器 → 反向代理(如 Nginx/HAProxy/Apache httpdmod_proxy)→ 應(yīng)用服務(wù)器(Node.js/Python/PHP/Java/Go 等)→ 數(shù)據(jù)庫(kù)或外部服務(wù)。只要在某一跳,等待上游的時(shí)間超過了該跳的超時(shí)設(shè)置,這一跳就會(huì)放棄等待并以 504 告終。例如 Nginxproxy_read_timeout 默認(rèn)是 60s,如果在兩次讀取上游數(shù)據(jù)之間超過這個(gè)時(shí)間沒有任何字節(jié)到達(dá),連接會(huì)被關(guān)閉,最終返回 504。(Nginx)

CDN 或云負(fù)載均衡也會(huì)產(chǎn)生類似含義的 504。比如 AWS Application Load Balancer 報(bào) HTTP 504 的常見成因包括:在連接超時(shí)窗口內(nèi)無法與目標(biāo)實(shí)例建立連接、建立連接后在空閑超時(shí)內(nèi)未收到響應(yīng)、或網(wǎng)絡(luò)訪問控制配置阻斷了回程端口。AWS CloudFront 也在轉(zhuǎn)發(fā)到源站超時(shí)或源站直接返回 504 時(shí)呈現(xiàn)該狀態(tài)碼。(AWS Documentation)

Cloudflare 一類的邊緣代理也會(huì)在無法與源站建立聯(lián)系或源站響應(yīng)過慢時(shí)返回 504,其官方故障文檔明確把 502/504 歸為與源站通信失敗的大類。(Cloudflare Docs)


這類超時(shí)到底由誰(shuí)決定、為什么會(huì)發(fā)生

不同組件有各自的超時(shí)鐘表:

  • 反向代理的讀取超時(shí)。例如 Nginx 使用 proxy_read_timeout 控制從上游讀取響應(yīng)的等待時(shí)間,默認(rèn) 60s。這不是整個(gè)響應(yīng)的總時(shí)長(zhǎng)限制,而是兩次讀操作之間的空閑間隔限制;如果上游持續(xù)輸出數(shù)據(jù),連接可以長(zhǎng)時(shí)間保持。(Nginx)
  • 負(fù)載均衡器的連接與空閑超時(shí)。ALB 典型情況下,連接建立階段等候有限的秒數(shù),連接建立后還受空閑超時(shí)控制,超過空閑時(shí)間沒流量就會(huì)斷開并可能觸發(fā) 504。(AWS Documentation)
  • 代理或應(yīng)用的長(zhǎng)連接策略。比如消息推送、WebSocket、SSE 這種長(zhǎng)連接,若沒有心跳或上游保持活躍,默認(rèn) 60s 的空閑超時(shí)就可能使連接被網(wǎng)關(guān)斷開。Nginx 指南也建議在這類場(chǎng)景提高 proxy_read_timeout 或讓上游定期發(fā)送心跳幀。(Nginx)
  • 其他網(wǎng)關(guān)的等待策略。HAProxy 手冊(cè)與常見問題中也提示:當(dāng)?shù)却蠖隧憫?yīng)超時(shí),會(huì)返回 504,需要結(jié)合日志與 timeout server、timeout connect 等配置分析與調(diào)整。(HAProxy Technologies)

一旦某一層的等待鐘走到了盡頭,而上游仍未送回頭部或數(shù)據(jù),就會(huì)觸發(fā) 504。原因可能是上游真的慢(CPU 頂滿、GC 卡頓、DB 查詢慢、鎖等待、線程池耗盡)、也可能是網(wǎng)絡(luò)路徑不通或安全組/ACL 設(shè)置讓回程報(bào)文發(fā)不回來,還可能是上游持續(xù)工作但長(zhǎng)時(shí)間沒有任何字節(jié)到達(dá)代理的讀緩沖,導(dǎo)致被判定為超時(shí)。(AWS Documentation)


作為訪問者,可以做的快速甄別

當(dāng)頁(yè)面拋出 504,很大概率并不是你的瀏覽器或本地網(wǎng)絡(luò)的鍋,而是站點(diǎn)一側(cè)各層之間的通信出問題了。你可以先輕量排查:刷新、換個(gè)網(wǎng)絡(luò)、關(guān)代理/VPN、稍后再試;若問題持續(xù)且只發(fā)生在某個(gè)特定網(wǎng)站,通常需要網(wǎng)站維護(hù)方處理。MDN 與多家運(yùn)維文檔都強(qiáng)調(diào),504 多半與服務(wù)器端超時(shí)相關(guān)。(MDN Web Docs, Kinsta?)


作為站點(diǎn)維護(hù)者,如何系統(tǒng)地定位與修復(fù)

把它當(dāng)作一次跨越多跳的延遲問題,從外到內(nèi)逐層壓縮不確定性。

觀測(cè)與證據(jù)

  • 在代理與負(fù)載均衡層啟用訪問與錯(cuò)誤日志,記錄上游地址、響應(yīng)時(shí)間、狀態(tài)。Nginx 的錯(cuò)誤日志里常見 upstream timed out ... while reading response header from upstream 的字樣,基本就是上游沒有在時(shí)限內(nèi)送回響應(yīng)頭。(Nginx)
  • ALBCloudFront 查看 ELB 訪問日志、CloudWatch 指標(biāo),核對(duì) TargetResponseTime、ELB 級(jí)別的 HTTP 504 計(jì)數(shù)峰值與實(shí)例側(cè)負(fù)載。(AWS Documentation)
  • 對(duì)上游應(yīng)用做端到端鏈路追蹤與慢事務(wù)分析,確認(rèn)是否存在極端長(zhǎng)尾請(qǐng)求。

連通性與配置

  • curl、mtr/traceroute、ping 在代理與上游之間做連通性與往返延遲測(cè)試,排除網(wǎng)絡(luò)層面的丟包或路由異常。實(shí)踐性指引也建議優(yōu)先確認(rèn)網(wǎng)絡(luò)路徑健康。(Netdata)
  • 檢查云環(huán)境的安全組與 NACL,確保負(fù)載均衡節(jié)點(diǎn)的臨時(shí)端口范圍允許回程,AWS 文檔列舉了這條常見誤配置。(AWS Documentation)
  • 審核代理的超時(shí)設(shè)置是否與業(yè)務(wù)相匹配。長(zhǎng)時(shí)間導(dǎo)出、視頻轉(zhuǎn)碼、復(fù)雜報(bào)表這類本就運(yùn)行較久的請(qǐng)求,要么提高等待窗口,要么改造為異步任務(wù)。Nginxproxy_read_timeout、proxy_connect_timeoutHAProxytimeout server、timeout connectApacheProxyTimeout 均需與應(yīng)用時(shí)長(zhǎng)對(duì)齊。(Nginx, HAProxy Technologies, Server Fault)

應(yīng)用與后端性能

  • 識(shí)別慢查詢、外部依賴的長(zhǎng)尾調(diào)用,給數(shù)據(jù)庫(kù)與下游接口加超時(shí)與重試上限,必要時(shí)設(shè)置斷路器,避免把所有線程綁死在不可預(yù)期的等待上。
  • 增加并發(fā)能力與容量:連接池、線程池、worker 數(shù)、實(shí)例水平擴(kuò)展。
  • 對(duì)長(zhǎng)輪詢與長(zhǎng)連接類業(yè)務(wù),使用心跳、分片響應(yīng)或升級(jí)為 WebSocket/SSE 并匹配更合適的空閑超時(shí)。(Nginx)

為什么你看到的是 504 而不是 502/503

這一組 5xx 常讓人困惑。MDN 的解釋可以簡(jiǎn)明對(duì)比:504 是沒等到上游的響應(yīng);502 是收到了,但格式或協(xié)議上不對(duì);503 是服務(wù)暫時(shí)不可用,常由限流或維護(hù)導(dǎo)致。這三者定位入口不一樣:504 優(yōu)先看鏈路延遲與超時(shí)配置,502 看協(xié)議與網(wǎng)關(guān)轉(zhuǎn)換,503 看容量與熔斷。(MDN Web Docs)


不同平臺(tái)的一點(diǎn)差異

現(xiàn)實(shí)世界里,邊緣層對(duì)超時(shí)的命名和細(xì)節(jié)有差異。CloudFront504 既可能是源站返回的 504 被透?jìng)鳎部赡苁沁吘壍却凑境瑫r(shí)自行生成;ALBHTTP 504 可能來自連接階段或空閑階段的超時(shí);Cloudflare 也把 502/504 統(tǒng)稱為與源站通信失敗,排查時(shí)需要對(duì)照各自的日志與指標(biāo)來識(shí)別歸因。(AWS Documentation, Cloudflare Docs)


可運(yùn)行的小示例:用一段 Node.js 代碼模擬 504

很多人沒有 NginxHAProxy 的環(huán)境,也能用一段小腳本快速體會(huì) 504 的本質(zhì):網(wǎng)關(guān)等不到上游。下面這段代碼一舉兩用:

  • http://localhost:9000/?delay=ms 提供一個(gè)故意延遲的上游服務(wù);
  • http://localhost:8000/ 提供一個(gè)簡(jiǎn)單代理,向上游轉(zhuǎn)發(fā)并設(shè)置 2s 超時(shí);
    當(dāng)你把上游延遲調(diào)到 3000ms,代理會(huì)在 2s 到點(diǎn)時(shí)自制一個(gè) 504 返回。

代碼只用單引號(hào),避免出現(xiàn)英文雙引號(hào);粘貼成 demo.js 后用 node demo.js 運(yùn)行。

// demo.js
const http = require('http');
const url = require('url');

const upstream = http.createServer((req, res) => {
  const q = url.parse(req.url, true).query;
  const delay = Number(q.delay || '0');
  setTimeout(() => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ ok: true, delay }));
  }, isNaN(delay) ? 0 : delay);
});

upstream.listen(9000, () => {
  console.log('upstream on :9000');
});

const proxy = http.createServer((clientReq, clientRes) => {
  const options = {
    hostname: '127.0.0.1',
    port: 9000,
    path: clientReq.url,
    method: clientReq.method,
    headers: clientReq.headers
  };

  const upstreamReq = http.request(options, upstreamRes => {
    clientRes.writeHead(upstreamRes.statusCode || 200, upstreamRes.headers);
    upstreamRes.pipe(clientRes);
  });

  // 2s 沒有拿到上游響應(yīng)頭就超時(shí),模擬網(wǎng)關(guān)的等待鐘
  upstreamReq.setTimeout(2000, () => {
    upstreamReq.destroy(new Error('upstream timeout'));
  });

  upstreamReq.on('error', err => {
    if (!clientRes.headersSent) {
      clientRes.writeHead(504, { 'Content-Type': 'text/plain' });
    }
    clientRes.end('504 Gateway Time-out: ' + err.message);
  });

  clientReq.pipe(upstreamReq);
});

proxy.listen(8000, () => {
  console.log('proxy on :8000, try:');
  console.log('  fast:  curl "http://127.0.0.1:8000/?delay=500"');
  console.log('  slow:  curl "http://127.0.0.1:8000/?delay=3000"');
});

運(yùn)行后:

  • curl 'http://127.0.0.1:8000/?delay=500' 能得到上游的 200。
  • curl 'http://127.0.0.1:8000/?delay=3000' 會(huì)返回 504 Gateway Time-out: upstream timeout。

這個(gè)小實(shí)驗(yàn)揭示了核心事實(shí):只要網(wǎng)關(guān)等待上游超過了自己的超時(shí)策略,就會(huì)觸發(fā) 504——無論上游是不是過一會(huì)兒才慢慢把結(jié)果算出來。


真實(shí)環(huán)境中的典型修復(fù)清單

把上面的分析落地成可執(zhí)行動(dòng)作:

  1. 把慢操作改造成異步
    報(bào)表導(dǎo)出、壓縮、視頻轉(zhuǎn)碼、跨系統(tǒng)聚合等,改為作業(yè)隊(duì)列與回調(diào)/輪詢式查詢狀態(tài)。網(wǎng)關(guān)的等待只負(fù)責(zé)快速確認(rèn)作業(yè)受理。

  2. 為上游和下游都設(shè)置合理的超時(shí)
    數(shù)據(jù)庫(kù)客戶端、HTTP 下游調(diào)用要有明確的連接超時(shí)與讀取超時(shí),還要設(shè)置重試上限,避免在網(wǎng)關(guān)之外形成更長(zhǎng)的隱性等待鏈。

  3. 同步長(zhǎng)連接的存活策略
    SSEWebSocket 的心跳頻率要覆蓋各層默認(rèn)空閑超時(shí);或在代理層提升空閑超時(shí)上限。Nginx 官方文檔明確指出,默認(rèn) 60s 的空閑期會(huì)關(guān)連接,可以用 proxy_read_timeout 增大,或由上游定時(shí)發(fā)送心跳。(Nginx)

  4. 按需調(diào)高代理與負(fù)載均衡時(shí)鐘
    示例:Nginxproxy_read_timeout 300s;HAProxytimeout server 5m、Apache httpdProxyTimeout 300;注意提升時(shí)限只是爭(zhēng)取時(shí)間,不是替代性能優(yōu)化。(Nginx, HAProxy Technologies, Server Fault)

  5. 擴(kuò)容與隔離
    把耗時(shí)接口拆分到獨(dú)立后端或隊(duì)列消費(fèi)者,給它單獨(dú)的池與伸縮規(guī)則,避免拖慢其他接口;在 ALB/NGW 層按目標(biāo)特性設(shè)置獨(dú)立監(jiān)聽與規(guī)則。AWS 的文檔同樣提示了連接與空閑時(shí)限的影響。(AWS Documentation)

  6. 網(wǎng)絡(luò)與安全配置一致性
    嚴(yán)格核對(duì)安全組、NACL、防火墻與回程端口范圍,確保存活探針、回源端口都能互通;AWS 的知識(shí)庫(kù)特別提到對(duì)臨時(shí)端口的放行。(AWS Documentation)


當(dāng)你用 NginxHAProxy、Apache 時(shí)的參考片段

  • Nginx:在反代 locationserver 上設(shè)定
location /api/ {
    proxy_pass http://app_upstream;
    proxy_connect_timeout 5s;
    proxy_read_timeout 300s;   # 兩次讀取之間允許的最大空閑
    proxy_send_timeout 60s;
}

說明文檔指出 proxy_read_timeout 的默認(rèn)值為 60s,且定義為兩次讀取之間的超時(shí),而不是整個(gè)響應(yīng)的總時(shí)長(zhǎng)。(Nginx)

  • HAProxy:常見的三個(gè)超時(shí)
defaults
  timeout connect 5s
  timeout client  60s
  timeout server  300s

server 超時(shí)過小,容易出現(xiàn) 504;官方運(yùn)維文檔建議結(jié)合訪問日志分析。(HAProxy Technologies)

  • Apache httpd:反向代理的總等待
<IfModule mod_proxy.c>
  ProxyTimeout 300
</IfModule>

ProxyTimeout 用于代理請(qǐng)求的超時(shí)控制,文檔與社區(qū)答復(fù)都給出了用法示例。(Server Fault)


把報(bào)錯(cuò)信息翻譯成人話

報(bào)錯(cuò)里那句英文 The gateway did not receive a timely response from the upstream server or application 可以理解為:
當(dāng)前節(jié)點(diǎn)并不是最終處理你請(qǐng)求的服務(wù),而是一個(gè)幫你轉(zhuǎn)發(fā)的中間人;它已經(jīng)把請(qǐng)求發(fā)出去了,但上游在它的耐心耗盡之前沒有回任何可用的數(shù)據(jù),于是它只能告訴瀏覽器:等太久了。這正是 RFC 9110 所定義的 504 語(yǔ)義。(IETF Datatracker)


小結(jié)與行動(dòng)建議

看到 504,就把注意力放在網(wǎng)關(guān)與它的上游之間的那段鏈路:它們是否連得上、等得起、兩端是否有心跳維持,應(yīng)用是否把耗時(shí)任務(wù)塞進(jìn)了同步流程。把證據(jù)留在日志與指標(biāo)里,用合理的超時(shí)和容量把邊界框出來,再用架構(gòu)手段削去長(zhǎng)尾。這樣做,504 會(huì)從頻繁煩擾,變成偶發(fā)可預(yù)期的告警。

延伸閱讀與權(quán)威參考
RFC 9110504 Gateway Timeout 原文定義;MDN 的狀態(tài)碼解釋;Nginx proxy_read_timeout 官方說明;AWS ALBCloudFront 關(guān)于 504 的排障頁(yè)面;Cloudflare 官方的 502/504 故障說明;HAProxy 官方運(yùn)維常見問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容