1、背景

nginx 作為 nexus 的反向代理,maven 配置的 nexus 地址指向的是 nginx

2、問題

mvn 編譯構建時候出現個別的包或 pom 文件下載返回 502 導致編譯失敗,在同一個 VPC 內的 mvn 客户端不會出現該問題,跨雲賬號通過雲企業網組網訪問的 mvn客户端會出現 502,10 次構建有 8-9 次出現,復現率很高,如果跳過 nginx直接訪問 nexus就沒問題

3、排查過程

nginx日誌如下

{"method":"GET","uri":"/nexus/content/groups/lkb-public/org/codehaus/plexus/plexus-component-annotations/1.5.5/plexus-component-annotations-1.5.5.pom.sha1",
"status":"502",
"X-Forwarded-For":"-",
"response_time":"7.012",
"request_time":"7.009",
"upstream_connect_time":"-",
"upstream_header_time":"-",
"userAgent":"Apache-Maven/3.6.3 (Java 1.8.0_333; Linux 5.15.0-142-generic)","request_length":"370","bodySize":"150","authorization":"-","x-auth-token":"-","EagleEye-TraceID":"-"}

nexus 查看 TCP 連接

    netstat -anptu

    有 500 多個連接,其中 200 多個 CLOSE_WAITE,其餘是ESTABLISHED

 

4、故障分析

由於 nginx之前未開啓長連接,默認都是短連接,mvn下載的每一個文件都會創建一個新的 TCP 連接,多個下載不會複用同一個連接,mvn編譯過程有幾百個 pom jar下載就會導致有幾百個 TCP 連接建立,這個數值對於 linux系統而已未達瓶頸,但是對 nexus而言已經到了它連接池的上線了,滿了之後新的請求過來只在等待隊列裏面,不會處理請求,nginx會對於這種建立 TCP 連接後無響應的連接在過了幾秒鐘後判斷為連接不可用或異常遂返回 502

5、解決

nginx增加如下配置參數

upstream repo{
;
    # 新增:長連接複用配置(解決偶發連接中斷)

 個空閒長連接

    keepalive_timeout 60s;    # 空閒連接超時(與 Nexus 適配)

;   # 單個長連接最大處理請求數
        }
...location / {
  proxy_pass http://repo;
  # 新增:啓用 HTTP/1.1 長連接(核心修復偶 502)
  proxy_http_version 1.1;
  proxy_set_header Connection ""; # 清空 Connection 頭,啓用 keepalive
}...

相關配置解釋

  proxy_http_version 1.1;  proxy_set_header Connection ""; 為了開啓長連接,可以理解為開關,但是僅僅如此還不夠,只是每次連接後 TCP 不會關閉,不會讓多個請求複用同一個 TCP 連接

keepalive 32; 是關鍵,永遠有 32 空閒 TCP 長連接存在,請求進來都複用這 32個,如果單個最大處理 100 個(keepalive_requests 100;)打滿後可以在 32 基礎上新增連接,沒有上限。如果沒有請求了,也不會釋放所以連接會保留 32 個,避免頻繁新建 TCP,較少三次握手等開銷

 

6、相關擴展

502 與 504 區別

502 和 504 是 Nginx 反向代理場景下最常見的兩個錯誤碼,核心區別在於 錯誤觸發的階段和原因:

  • 502 = 上游服務器「連接失敗 / 處理異常」(網關能連但上游 “壞了”);
  • 504 = 上游服務器「連接成功但響應超時」(網關連得上但上游 “太慢 / 沒回應”)。

結合你的 Nexus 代理場景(跨 VPC、長連接 / 短連接、超時配置),用「通俗類比 + 技術拆解 + 場景對應」的方式,把兩者的區別講透:

 核心區別總表(一目瞭然)

為何NGINX 報502錯 -php教程_TCP

 

TCP 中的 CLOSE_WAIT 、TIME_WAIT 存在場景

TCP 連接的建立(三次握手)和關閉(四次揮手)過程中,客户端與服務端會經歷一系列明確的狀態變遷。這張圖能清晰展示完整流程與對應狀態:

TCP 三次握手(建立連接)

  1. 客户端:從 CLOSED 發起連接,發送 SYN 包後進入 SYN_SENT 狀態,等待服務器確認。
  2. 服務端:在 LISTEN 狀態下收到 SYN 包,回覆 SYN+ACK 包後進入 SYN_RCVD 狀態。
  3. 客户端:收到 SYN+ACK 後,發送 ACK 包並立即進入 ESTABLISHED 狀態。
  4. 服務端:收到最終的 ACK 包後,也進入 ESTABLISHED 狀態,雙方開始傳輸數據。

TCP 四次揮手(關閉連接)

  1. 主動關閉方(如客户端):發送 FIN 包,進入 FIN_WAIT_1 狀態,等待對方的 ACK
  2. 被動關閉方(如服務端):收到 FIN 包後發送 ACK 確認,進入 CLOSE_WAIT 狀態(此時仍可發送剩餘數據)。
  3. 主動關閉方:收到 ACK 後進入 FIN_WAIT_2 狀態,等待對方的 FIN 包。
  4. 被動關閉方:完成數據發送後,發送自己的 FIN 包,進入 LAST_ACK 狀態,等待最後的 ACK
  5. 主動關閉方:收到 FIN 包後發送 ACK 確認,並進入 TIME_WAIT 狀態(等待 2MSL 時間,確保對方收到 ACK)。
  6. 被動關閉方:收到最終的 ACK 後,立即進入 CLOSED 狀態。
  7. 主動關閉方:TIME_WAIT 超時後,進入 CLOSED 狀態,連接徹底關閉。

核心狀態速查表

為何NGINX 報502錯 -php教程_nginx_02

 

根據上述分析,time_wait屬於正常情況,如果有大量短鏈接的話由於有釋放時長(2MSL)短期內會有大量的此種狀態

close_wait如果大量存在屬於異常狀況,説明被動關閉接收方沒有進行後續的回包