服務端 TCP 連線 TIME_WAIT 怎麼破?

語言: CN / TW / HK

點選進入“PHP開源社群”    

免費獲取進階面試、文件、影片資源

問題描述

模擬高併發的場景,會出現批量的 TIME_WAIT 的 TCP 連線:

短時間後,所有的 TIME_WAIT 全都消失,被回收,埠包括服務,均正常。即,在高併發的場景下,TIME_WAIT 連線存在,屬於正常現象。

線上場景中,持續的高併發場景:

  • 一部分 TIME_WAIT 連線被回收,但新的 TIME_WAIT 連線產生;

  • 一些極端情況下,會出現大量的 TIME_WAIT 連線。

Think:上述大量的 TIME_WAIT 狀態 TCP 連線,有什麼業務上的影響嗎?

Nginx 作為反向代理時,大量的短連結,可能導致 Nginx 上的 TCP 連線處於 time_wait 狀態:

  • 每一個 time_wait 狀態,都會佔用一個「本地埠」,上限為 65535(16 bit,2 Byte);

  • 當大量的連線處於 time_wait 時,新建立 TCP 連線會出錯,address already in use : connect 異常

統計 TCP 連線的狀態:

// 統計:各種連線的數量

$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

ESTABLISHED 1154

TIME_WAIT 1645

Tips:TCP 本地埠數量,上限為 65535(6.5w),這是因為 TCP 頭部使用 16 bit,儲存「埠號」,因此約束上限為 65535。

問題分析

大量的 TIME_WAIT 狀態 TCP 連線存在,其本質原因是什麼?

  • 大量的短連線存在

  • 特別是 HTTP 請求中,如果 connection 頭部取值被設定為 close 時,基本都由「服務端」發起主動關閉連線

  • 而 TCP 四次揮手關閉連線機制中,為了保證 ACK 重發和丟棄延遲資料,設定 time_wait 為 2 倍的 MSL(報文最大存活時間)

TIME_WAIT 狀態:

  • TCP 連線中,主動關閉連線的一方出現的狀態;(收到 FIN 命令,進入 TIME_WAIT 狀態,並返回 ACK 命令)

  • 保持 2 個 MSL 時間,即,4 分鐘;(MSL 為 2 分鐘)

解決辦法

解決上述 time_wait 狀態大量存在,導致新連線建立失敗的問題,一般解決辦法:

  • 客戶端,HTTP 請求的頭部,connection 設定為 keep-alive,保持存活一段時間:現在的瀏覽器,一般都這麼進行了

  • 伺服器端,允許 time_wait 狀態的 socket 被重用、縮減 time_wait 時間,設定為 1 MSL(即,2 mins)

結論:幾個核心要點

time_wait 狀態的影響:

  • TCP 連線中,「主動發起關閉連線」的一端,會進入 time_wait 狀態

  • time_wait 狀態,預設會持續 2 MSL(報文的最大生存時間),一般是 2x2 mins

  • time_wait 狀態下,TCP 連線佔用的埠,無法被再次使用

  • TCP 埠數量,上限是 6.5w(65535,16 bit)

  • 大量 time_wait 狀態存在,會導致新建 TCP 連線會出錯,address already in use : connect 異常

現實場景:

  • 伺服器端,一般設定:不允許「主動關閉連線」

  • 但 HTTP 請求中,http 頭部 connection 引數,可能設定為 close,則,服務端處理完請求會主動關閉 TCP 連線

  • 現在瀏覽器中, HTTP 請求 connection 引數,一般都設定為 keep-alive

  • Nginx 反向代理場景中,可能出現大量短連結,伺服器端,可能存在。

解決辦法:

  • 伺服器端允許 time_wait 狀態的 socket 被重用

  • 縮減 time_wait 時間,設定為 1 MSL(即,2 mins)

附錄

幾個方面:

  • TCP 連線狀態的查詢

  • MSL 時間

  • TCP 三次握手和四次握手

附錄 A:查詢 TCP 連線狀態

Mac 下,查詢 TCP 連線狀態的具體命令:

// Mac 下,查詢 TCP 連線狀態
$ netstat -nat |grep TIME_WAIT

// Mac 下,查詢 TCP 連線狀態,其中 -E 表示 grep 或的匹配邏輯
$ netstat -nat | grep -E "TIME_WAIT|Local Address"
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 127.0.0.1.1080 127.0.0.1.59061 TIME_WAIT

// 統計:各種連線的數量
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 1154
TIME_WAIT 1645

附錄 B:MSL 時間

MSL,Maximum Segment Lifetime,“報文最大生存時間”

  • 任何報文在網路上存在的最長時間,超過這個時間報文將被丟棄。(IP 報文)

  • TCP報文 (segment)是ip資料報(datagram)的資料部分。

Tips:RFC 793中規定MSL為2分鐘,實際應用中常用的是30秒,1分鐘和2分鐘等。

2MSL,TCP 的 TIME_WAIT 狀態,也稱為2MSL等待狀態:

  • 當TCP的一端發起主動關閉(收到 FIN 請求),在發出最後一個ACK 響應後,即第3次握 手完成後,傳送了第四次握手的ACK包後,就進入了TIME_WAIT狀態。

  • 必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要目的是怕最後一個 ACK包對方沒收到,那麼對方在超時後將重發第三次握手的FIN包,主動關閉端接到重發的FIN包後,可以再發一個ACK應答包。

  • 在 TIME_WAIT 狀態時,兩端的埠不能使用,要等到2MSL時間結束,才可繼續使用。(IP 層)

  • 當連線處於2MSL等待階段時,任何遲到的報文段都將被丟棄。不過在實際應用中,可以通過設定 「SO_REUSEADDR選項」,達到不必等待2MSL時間結束,即可使用被佔用的埠。

附錄 C:TCP 三次握手和四次握手

具體示意圖:

  • 三次握手,建立連線過程

  • 四次揮手,釋放連線過程

如果你年滿18週歲以上,又覺得學【PHP】太難?想嘗試其他程式語言,那麼我推薦你學Python,現有價值499元Python零基礎課程限時免費領取,限10個名額!

掃描二維碼-免費領取

點選“檢視原文”獲取更多