tcp TIME_WAIT
進入主題前必須做鋪墊啊,講講TIME_WAIT
.因爲TCP連接是雙向的,所以在關閉連接的時候,兩個方向各自都需要關閉。先發FIN
包的一方執行的是主動關閉;後發FIN
包的一方執行的是被動關閉。主動關閉的一方會進入TIME_WAIT
狀態,並且在此狀態停留兩倍的MSL
(最大報文存活時間,一般Linux內核設置30秒)時長。
爲什麼主動方要傻乎乎等2MSL呢?不等,行不行?
TCP目的是可靠傳輸,主動關閉的一方發出FIN
,被動方回覆ACK
,接着被動方發送FIN
,主動方收到被動關閉的一方發出的FIN
包後,迴應ACK
包,同時進入TIME_WAIT
狀態,但是因爲網絡原因,主動關閉的一方發送的這個ACK包很可能延遲,從而觸發被動連接一方重傳FIN包。極端情況下,這一去(ACK
去被動方)一回(重傳FIN
回來),就是兩倍的MSL
時長。
如果主動關閉的一方跳過TIME_WAIT
直接進入CLOSED
,或者在TIME_WAIT
停留的時長不足兩倍的MSL
,那麼當被動關閉的一方早先發出的FIN
延遲包到達或者重傳FIN
包到達後,就可能出現類似下面的問題:
- 主動方舊的TCP連接已經不存在了,主動方只能返回
RST
包 - 主動方新的TCP連接被建立起來了,延遲包可能干擾新的連接
所以, TIME_WAIT
必須等,2MSL不能少
減少TIME_WAIT
TIME_WAIT
期間,資源不會釋放,現在都追求高性能高併發,快速釋放資源是躲不掉的.對於客戶端因爲有端口65535問題,TIME_WAIT
過多直接影響處理能力. 對於服務器,無端口數量限制的問題,Linux優化也很給力,每個處於TIME_WAIT
狀態下連接內存消耗很少, 而且也能通過tcp_max_tw_buckets
=
${你要的閾值} 配置最大上限,但是對於短連接爲主的web服務器,幾十萬的連接,基數很大,耗得內存也不小哦.快速釋放總是好的
-
tcp_tw_recycle
:回收TIME_WAIT
連接
對客戶端和服務器同時起作用,開啓後在 3.5*RTO 內回收,RTO 200ms~ 120s 具體時間視網絡狀況。RTO
(Retransmission TimeOut)重傳超時時間.內網狀況比tcp_tw_reuse
稍快,公網尤其移動網絡大多要比tcp_tw_reuse
慢,優點就是能夠回收服務端的TIME_WAIT
數量但是,有個小坑:當多個客戶端通過NAT方式聯網並與服務端交互時,服務端看到的是同一個IP,也就是說對服務端而言這些客戶端實際上等同於一個,可惜由於這些客戶端的時間戳可能存在差異,於是乎從服務端的視角看,便可能出現時間戳錯亂的現象,進而直接導致時間戳小的數據包被丟棄。客戶端處於NAT很常見,基本公司家庭網絡都走NAT.
-
tcp_tw_reuse
:複用TIME_WAIT連接 只對客戶端起作用,1秒後
才能複用,當創建新連接的時候,如果可能的話會考慮複用相應的TIME_WAIT
連接。通常認爲tcp_tw_reuse
比tcp_tw_recycle
安全一些,這是因爲一來TIME_WAIT
創建時間必須超過一秒
纔可能會被複用;二來只有連接的時間戳是遞增的時候纔會被複用。
客戶端請求服務器,服務器響應後主動關閉連接,TIME_WAIT
存在於服務器,服務器是被連接者,沒有複用一說,所以只對客戶端起作用.如果是客戶端主動關閉,TIME_WAIT
存在於客戶端,這個時候再次連接服務器,可以複用之前TIME_WAIT
留下的半廢品.
tcp_timestamps
:以上兩點,必須在客戶端和服務端timestamps 開啓時才管用(默認開啓) 需要根據timestamp的遞增性來區分是否新連接
總結
- 客戶端
tcp_tw_reuse
複用連接管用,tcp_tw_recycle
有用,但是客戶端主要不是接受連接,用處不大 - 服務器
tcp_tw_recycle
回收連接管用,tcp_tw_reuse
複用無效.爲了減少TIME_WAIT
留在服務器,可以在服務器開啓KeepAlive
,儘量不讓服務器主動關閉,而是客戶端主動關閉,減少TIME_WAIT
產生.
More
tcp_tw_reuse
和SO_REUSEADDR
說的完全不是一個東西.
打個例子,舉個比方: 一般來講,在我們寫一個server的時候,會用到SO_REUSEADDR
,因爲,當server正在工作的時候,需要重啓,這時候,可能server的一些子進程還在工作,也或者一些連接還處於time_wait
狀態,而且time_wait
狀態的時間一般比較長,如果沒有SO_REUSEADDR
,則會因爲端口被佔用而bind失敗,無法啓動server.
這種模式下典型例子Nginx
,一個master進程監聽一個端口,接受連接,將邏輯工作分發給子進程.顯然,單進程監聽分發存在瓶頸,能不能多個進程監聽同一個端口呢?
SO_REUSEPORT
Linux kernel 3.9
帶來了SO_REUSEPORT
特性,就是解決上面的問題,讓多個進程同時監聽同一個端口,幹掉單點監聽瓶頸,允許端口重複捆綁