服務端 TCP 連線 TIME_WAIT 怎麼破?
點選進入“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個名額!
▲ 掃描二維碼-免費領取
點選“檢視原文”獲取更多
- 用“最好的語言”PHP,做一個機器學習資料集
- PHP開發中,如何做錯誤與異常處理呢 ?
- phper如何用Rust開發PHP擴充套件Liunx版【詳細教程】
- PHP 使用 CURL 詳解
- 小程式如何使用訂閱訊息(PHP程式碼 小程式js程式碼)
- MySQL提高效能,緩解資料庫壓力,你會做讀寫分離嗎?
- 深入理解 glibc malloc:記憶體分配器實現原理
- Laravel 成為最佳 PHP 框架的 14 個理由!
- 7 款顏值 yyds 的 Linux 作業系統 !
- 一文了解“最好程式語言”PHP 必知的 16 個程式設計法則!
- 終於有人把 "單點" 登入說清楚了!
- PHP程式執行Python指令碼(接收資料及傳參)
- 服務端 TCP 連線 TIME_WAIT 怎麼破?
- Shell 分析日誌檔案命令全面總結!
- 有些PHP程式設計師,不知道如何有效的除錯BUG
- 10個你可能不曾用過卻很有用的 Linux 命令
- PHP如何解決百萬級全站使用者訊息推送問題
- PHP Redis快取技術一覽
- Seata-php 入門與下半年展望
- 3 個PHP 知識總結:Memcache、快取和正則