靚仔! 能跳出TIME-WAIT的坑嗎

  • 2019 年 11 月 28 日
  • 筆記

1. 開篇語

在TCP斷開連接四次揮手時, 主動發起關閉方會產生 TIME_WAIT, TIME_WAIT 是 TCP 協議可靠性設計的重要一個環節, 雖說增強了可靠性, 但是對於高並發場景下, 會產生大量的 TIME_WAIT, 導致高峰時段無埠可以使用.

本文只做簡單學習測試, 不保證內容的全面性及正確性, 不要輕易修改正式環境內核配置

今天主要對兩個 Linux 內核的配置 tcp_tw_reuse 和 tcp_tw_recycle 進行測試講解

2. 搭建實驗環境

為了方面模擬網路情況, 我們設置可用埠區間僅為81

sysctl -w "net.ipv4.ip_local_port_range=81 81"

3. 默認配置測試

訪問本地nginx服務

curl http://127.0.0.1    curl http://127.0.0.1  curl: (7) Failed to connect to 127.0.0.1: Cannot assign requested address

查看網路狀態

netstat -napo |grep 127.0.0.1

我們可以看到, 第一次正常, 在 2MSL 時間內, 再次訪問將會出現無法分配請求地址錯誤. 在 Linux 中TIME_WAIT時間為60s,並且還無法修改

TIME_WAIT過期時間宏定義

//include/net/tcp.h  /* how long to wait to destroy TIME-WAIT   * state, about 60 seconds   */  #define TCP_TIMEWAIT_LEN (60*HZ) 

驗證時間

可以看到標紅線的地方為 time_wait 的倒計時,到時間後將會自動釋放埠.

4. 關於 TIME_WAIT 情況的配置指令

開啟 tcp_tw_reuse

echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

進行測試

我們可以觀測到, 在 TIME_WAIT 狀態的埠也可以繼續完成請求, 但不會改變 TIME_WAIT 本身的狀態和計時.

此結論證明, 當本地埠將耗盡時, 可以嘗試開啟 tcp_tw_reuse 進行埠重用.

It's not SO_REUSEADDR socket option. SO_REUSEADD is used for binding socket to LISTEN state even if it is in TIME_WAIT state.

開啟 tcp_tw_recycle

開啟 tcp_tw_recycle

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

進行測試

結果很好, 同樣完成了請求測試. 但是似乎跟 tcp_tw_reuse 哪裡不太一樣?

對比開啟 tcp_tw_reuse 的netstat檢測結果, 看看有什麼差異.

我們發現這裡簡直就是暴力美學, 根本沒有 TIME_WAIT 的狀態呀

linux內核判定程式碼

當開啟回收時,我們的 timeout 值為 rto, 這是一個非常短的一個時間, 否則為 TCP_TIMEWAIT_LEN , 還記得文章開頭提到的宏定義的時間嗎, 沒錯, 這裡指的就是那個60s.

5. tcp_tw_reuse 與 tcp_tw_recycle 的區別

似乎這兩個參數都能夠很好的工作, 至少測試結果是很理想的.

參數

功能

tcp_tw_reuse

復用(reuse),不改變 TIMEWAIT 狀態

tcp_tw_recycle

回收(recycle),最快時間回收

net.ipv4.tcp_timestamps 默認開啟, 他是記錄標記時間戳

tcp_tw_reuse 是怎麼工作的

如果開啟了 tcp_tw_reuse, 如果客戶端發來的時間戳大於先前連接內核記錄的最新時間戳, 則 Linux 將重新使用狀態中的現有連接以 TIME-WAIT 用於新的對外請求連接, 狀態中的傳出連接 TIME-WAIT可在僅一秒之後重複使用.

tcp_tw_recycle 是怎麼工作的

如果開啟了 tcp_tw_recycle, 則內核會記住客戶端上次發來數據包的時間戳, 如果發來的數據包時間戳小於內核記錄的最後發來的數據包時間戳, 那麼將會丟棄此數據包, 這種情況在 NAT 模式下多機器時間滯後或同時發送, 會有很大危險, 會造成難以排查的異常情況.

網上說 設置net.ipv4.tcp_timestamps=0, 可以不再檢測時間戳, 未找到官方出處, 真實性無法保證.

6. 結論:

在伺服器端,請勿啟用net.ipv4.tcp_tw_recycle 除非你非常確定你的服務中永遠不會有NAT設備。 啟用net.ipv4.tcp_tw_reuse 對於發送請求 (outgoing connection) 的連接有效。

在客戶端,啟用 net.ipv4.tcp_tw_reuse 是另一個幾乎安全的解決方案

而且,在設計協議時,不要先讓客戶關閉。客戶端不必處理將TIME-WAIT狀態推向更適合處理此問題的伺服器的狀態。

所以最終建議可以開啟 tcp_tw_reuse, 禁用 tcp_tw_recycle.

7. 附TCP狀態圖

參考資料:

https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux#netipv4tcp_tw_reuse

http://linuxsyseng.blogspot.com/2017/03/the-difference-with-tcptwrecycle-and.html