靚仔! 能跳出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