為什么要有TIME_WAIT?
TIME_WAIT是TCP主動關閉連接一方的一個狀態(tài)准浴,TCP斷開連接的時序圖如下:
當主動斷開連接的一方(Initiator)發(fā)送FIN包給對方萍程,且對方回復了ACK+FIN罩抗,然后Initiator回復了ACK后就進入TIME_WAIT狀態(tài)刨摩,一直將持續(xù)2MSL后進入CLOSED狀態(tài)株旷。
那么蛮粮,我們來看如果Initiator不進入TIME_WAIT狀態(tài)而是直接進入CLOSED狀態(tài)會有什么問題耕陷?
考慮這種情況,服務器運行在80端口辙喂,客戶端使用的連接端口是12306捶牢,數據傳輸完畢后服務端主動關閉連接,但是沒有進入TIME_WAIT巍耗,而是直接計入CLOSED了秋麸。這時,客戶端又通過同樣的端口12306與服務端建立了一個新的連接炬太。假如上一個連接過程中網絡出現了異常灸蟆,導致了某個包重傳并延時到達了服務端,這時服務端就無法區(qū)分這個包是上一個連接的還是這個連接的亲族。所以炒考,主動關閉連接一方要等待2MSL,然后才能CLOSE霎迫,保證連接中的IP包都要么傳輸完成斋枢,要么被丟棄了。
TIME_WAIT會帶來什么問題
系統(tǒng)中TIME_WAIT的連接數很多知给,會導致什么問題呢瓤帚?這要分別針對客戶端和服務器端來看的。
首先炼鞠,如果是客戶端發(fā)起了連接缘滥,傳輸完數據然后主動關閉了連接,這時這個連接在客戶端就會處于TIMEWAIT狀態(tài)谒主,同時占用了一個本地端口朝扼。如果客戶端使用短連接請求服務端的資源或者服務,客戶端上將有大量的連接處于TIMEWAIT狀態(tài)霎肯,占用大量的本地端口擎颖。最壞的情況就是榛斯,本地端口都被用光了,這時將無法再建立新的連接搂捧。
針對這種情況驮俗,對應的解決辦法有2個:
- 使用長連接,如果是http允跑,可以使用keepalive
- 增加本地端口可用的范圍王凑,比如Linux中調整內核參數:net.ipv4.ip_local_port_range
對于服務器而已,由于服務器是被動等待客戶端建立連接的聋丝,因此即使服務器端有很多TIME_WAIT狀態(tài)的連接索烹,也不存在本地端口耗盡的問題。大量的TIME_WAIT的連接會導致如下問題:
- 內存占用:因為每一個TCP連接都會有占用一些內存弱睦。
- 在某些Linux版本上可能導致性能問題百姓,因為數據包到達服務器的時候,內核需要知道數據包是屬于哪個TCP連接的况木,在某些Linux版本上可能會遍歷所有的TCP連接垒拢,所以大量TIME_WAIT的連接將導致性能問題。不過火惊,現在的內核都對此進行了優(yōu)化(待確認)求类。
那系統(tǒng)中處于TIME_WAIT狀態(tài)的TCP連接數有上限嗎?有的矗晃,這是通過net.ipv4.tcp_max_tw_buckets參數來控制的仑嗅,默認值為180000。當超過了以后张症,系統(tǒng)就開始關閉這些連接,同時會在系統(tǒng)日志中打印日志鸵贬。此時俗他,可以將這個值調大一些,從這個參數的默認值就可以看出阔逼,對服務器而已兆衅,處于TIME_WAIT狀態(tài)的TCP連接多點也沒有什么關系,只是多占用些內存而已嗜浮。
常見的TIMEWAIT錯誤參數
如果用TIME_WAIT作為關鍵字到網絡上搜索羡亩,會得到很多關于如何減少TIME_WAIT數量的建議,其中有些建議是有錯誤或者有風險的危融,列舉如下:
- net.ipv4.tcp_syncookies = 1畏铆,這個參數表示開啟SYN Cookies。當出現SYN等待隊列溢出時吉殃,啟用cookies來處理辞居,可防范少量SYN攻擊楷怒。這個和TIME_WAIT沒有什么關系。
- net.ipv4.tcp_tw_reuse = 1瓦灶,這個參數表示重用TIME_WAIT的連接鸠删,重用的條件是TCP的4元組(源地址、源端口贼陶、目標地址刃泡、目標端口)要完全一致,而且開啟了net.ipv4.tcp_timestamps碉怔,且新建立連接的使用的timestamp要大于當前連接的timestamp烘贴。所以,開啟了這個參數對減少TIME_WAIT的TCP連接有點用眨层,但條件太苛刻庙楚,所以實際用處不大。
- net.ipv4.tcp_tw_recycle = 1趴樱,這個參數表示開啟TIME_WAIT回收功能馒闷,開啟了這個參數后,將大大減小TIME_WAIT進入CLOSED狀態(tài)的時間叁征。但是開啟了這個功能了風險很大纳账,可能會導致處于NAT后面的某些客戶端無法建立連接。因為捺疼,開啟這個功能后疏虫,它要求來自同一個IP的TCP新連接的timestamp要大于之前連接的timestamp。
TIMEWAIT的“正確”處理方法
簡單總結一下我對于TIME_WAIT狀態(tài)TCP連接的理解和處理方法:
- TIME_WAIT狀態(tài)的設計初衷是為了保護我們的啤呼。服務端不必擔心系統(tǒng)中有幾w個處于TIME_WAIT狀態(tài)的TCP連接卧秘。可以調大net.ipv4.tcp_max_tw_buckets這個參數官扣。
- 使用短連接的客戶端翅敌,需要關注TIME_WAIT狀態(tài)的TCP連接,建議是采用長連接惕蹄,同時調節(jié)參數net.ipv4.ip_local_port_range蚯涮,增加本地可用端口的范圍。
- 可以開啟net.ipv4.tcp_tw_reuse這個參數卖陵,但是實際用處有限遭顶。
- 不要開啟net.ipv4.tcp_tw_recycle這個參數,它帶來的問題比用處大泪蔫。
- 某些Linux的發(fā)行版可以調節(jié)TIME_WAIT到CLOSED的等待時間(比如Ali的Linux內核提供了參數net.ipv4.tcp_tw_timeout )棒旗,可以稍微調小一點這個參數。