一、 time_wait的做用:java
TIME_WAIT狀態存在的理由: 1)可靠地實現TCP全雙工鏈接的終止 在進行關閉鏈接四次揮手協議時,最後的ACK是由主動關閉端發出的,若是這個最終的ACK丟失,服務器將重發最終的FIN, 所以客戶端必須維護狀態信息容許它重發最終的ACK。若是不維持這個狀態信息,那麼客戶端將響應RST分節,服務器將此分節解釋成一個錯誤(在java中會拋出connection reset的SocketException)。 於是,要實現TCP全雙工鏈接的正常終止,必須處理終止序列四個分節中任何一個分節的丟失狀況,主動關閉的客戶端必須維持狀態信息進入TIME_WAIT狀態。 2)容許老的重複分節在網絡中消逝 TCP分節可能因爲路由器異常而「迷途」,在迷途期間,TCP發送端可能因確認超時而重發這個分節,迷途的分節在路由器修復後也會被送到最終目的地,這個原來的迷途分節就稱爲lost duplicate。 在關閉一個TCP鏈接後,立刻又從新創建起一個相同的IP地址和端口之間的TCP鏈接,後一個鏈接被稱爲前一個鏈接的化身(incarnation),那麼有可能出現這種狀況,前一個鏈接的迷途重複分組在前一個鏈接終止後出現,從而被誤解成從屬於新的化身。 爲了不這個狀況,TCP不容許處於TIME_WAIT狀態的鏈接啓動一個新的化身,由於TIME_WAIT狀態持續2MSL,就能夠保證當成功創建一個TCP鏈接的時候,來自鏈接先前化身的重複分組已經在網絡中消逝。
二、大量TIME_WAIT形成的影響:web
在高併發短鏈接的TCP服務器上,當服務器處理完請求後馬上主動正常關閉鏈接。這個場景下會出現大量socket處於TIME_WAIT狀態。若是客戶端的併發量持續很高,此時部分客戶端就會顯示鏈接不上。
我來解釋下這個場景。主動正常關閉TCP鏈接,都會出現TIMEWAIT。數組
爲何咱們要關注這個高併發短鏈接呢?有兩個方面須要注意:
1. 高併發可讓服務器在短期範圍內同時佔用大量端口,而端口有個0~65535的範圍,並非不少,刨除系統和其餘服務要用的,剩下的就更少了。
2. 在這個場景中,短鏈接表示「業務處理+傳輸數據的時間 遠遠小於 TIMEWAIT超時的時間」的鏈接。服務器
這裏有個相對長短的概念,好比取一個web頁面,1秒鐘的http短鏈接處理完業務,在關閉鏈接以後,這個業務用過的端口會停留在TIMEWAIT狀態幾分鐘,而這幾分鐘,其餘HTTP請求來臨的時候是沒法佔用此端口的(佔着茅坑不拉翔)。單用這個業務計算服務器的利用率會發現,服務器幹正經事的時間和端口(資源)被掛着沒法被使用的時間的比例是 1:幾百,服務器資源嚴重浪費。(說個題外話,從這個意義出發來考慮服務器性能調優的話,長鏈接業務的服務就不須要考慮TIMEWAIT狀態。同時,假如你對服務器業務場景很是熟悉,你會發現,在實際業務場景中,通常長鏈接對應的業務的併發量並不會很高。
綜合這兩個方面,持續的到達必定量的高併發短鏈接,會使服務器因端口資源不足而拒絕爲一部分客戶服務。同時,這些端口都是服務器臨時分配,沒法用SO_REUSEADDR選項解決這個問題。cookie
關於time_wait的反思:網絡
存在便是合理的,既然TCP協議能盛行四十多年,就證實他的設計合理性。因此咱們儘量的使用其本來功能。
依靠TIME_WAIT狀態來保證個人服務器程序健壯,服務功能正常。
那是否是就不要性能了呢?並非。若是服務器上跑的短鏈接業務量到了我真的必須處理這個TIMEWAIT狀態過多的問題的時候,個人原則是儘可能處理,而不是跟TIMEWAIT幹上,非先除之然後快。
若是儘可能處理了,仍是解決不了問題,仍然拒絕服務部分請求,那我會採起負載均衡來抗這些高併發的短請求。持續十萬併發的短鏈接請求,兩臺機器,每臺5萬個,應該夠用了吧。通常的業務量以及國內大部分網站其實並不須要關注這個問題,一句話,達不到時才須要關注這個問題的訪問量。
小知識點:併發
TCP協議發表:1974年12月,卡恩、瑟夫的第一份TCP協議詳細說明正式發表。當時美國國防部與三個科學家小組簽訂了完成TCP/IP的協議,結果由瑟夫領銜的小組捷足先登,首先制定出了經過詳細定義的TCP/IP協議標準。當時做了一個試驗,將信息包經過點對點的衛星網絡,再經過陸地電纜
,再經過衛星網絡,再由地面傳輸,貫串歐洲和美國,通過各類電腦系統,全程9.4萬千米居然沒有丟失一個數據位,遠距離的可靠數據傳輸證實了TCP/IP協議的成功。
三、案列分析:負載均衡
首先,根據一個查詢TCP鏈接數,來講明這個問題。socket
netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}' LAST_ACK 14 SYN_RECV 348 ESTABLISHED 70 FIN_WAIT1 229 FIN_WAIT2 30 CLOSING 33 TIME_WAIT 18122
狀態描述:tcp
CLOSED:無鏈接是活動的或正在進行
LISTEN:服務器在等待進入呼叫
SYN_RECV:一個鏈接請求已經到達,等待確認
SYN_SENT:應用已經開始,打開一個鏈接
ESTABLISHED:正常數據傳輸狀態
FIN_WAIT1:應用說它已經完成
FIN_WAIT2:另外一邊已贊成釋放
ITMED_WAIT:等待全部分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另外一邊已初始化一個釋放
LAST_ACK:等待全部分組死掉
命令解釋:
先來看看netstat: netstat -n Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 123.123.123.123:80 234.234.234.234:12345 TIME_WAIT 你實際執行這條命令的時候,可能會獲得成千上萬條相似上面的記錄,不過咱們就拿其中的一條就足夠了。 再來看看awk: /^tcp/ 濾出tcp開頭的記錄,屏蔽udp, socket等無關記錄。 state[]至關於定義了一個名叫state的數組 NF 表示記錄的字段數,如上所示的記錄,NF等於6 $NF 表示某個字段的值,如上所示的記錄,$NF也就是$6,表示第6個字段的值,也就是TIME_WAIT state[$NF]表示數組元素的值,如上所示的記錄,就是state[TIME_WAIT]狀態的鏈接數 ++state[$NF]表示把某個數加一,如上所示的記錄,就是把state[TIME_WAIT]狀態的鏈接數加一 END 表示在最後階段要執行的命令 for(key in state) 遍歷數組
如何儘可能處理TIMEWAIT過多?
編輯內核文件/etc/sysctl.conf,加入如下內容:
net.ipv4.tcp_syncookies = 1 表示開啓SYN Cookies。當出現SYN等待隊列溢出時,啓用cookies來處理,可防範少許SYN攻擊,默認爲0,表示關閉; net.ipv4.tcp_tw_reuse = 1 表示開啓重用。容許將TIME-WAIT sockets從新用於新的TCP鏈接,默認爲0,表示關閉; net.ipv4.tcp_tw_recycle = 1 表示開啓TCP鏈接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉。 net.ipv4.tcp_fin_timeout 修改系默認的 TIMEOUT 時間
而後執行 /sbin/sysctl -p 讓參數生效.
/etc/sysctl.conf是一個容許改變正在運行中的Linux系統的接口,它包含一些TCP/IP堆棧和虛擬內存系統的高級選項,修改內核參數永久生效。
簡單來講,就是打開系統的TIMEWAIT重用和快速回收。
若是以上配置調優後性能還不理想,可繼續修改一下配置:
vi /etc/sysctl.conf net.ipv4.tcp_keepalive_time = 1200 #表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改成20分鐘。 net.ipv4.ip_local_port_range = 1024 65000 #表示用於向外鏈接的端口範圍。缺省狀況下很小:32768到61000,改成1024到65000。 net.ipv4.tcp_max_syn_backlog = 8192 #表示SYN隊列的長度,默認爲1024,加大隊列長度爲8192,能夠容納更多等待鏈接的網絡鏈接數。 net.ipv4.tcp_max_tw_buckets = 5000 #表示系統同時保持TIME_WAIT套接字的最大數量,若是超過這個數字,TIME_WAIT套接字將馬上被清除並打印警告信息。 默認爲180000,改成5000。對於Apache、Nginx等服務器,上幾行的參數能夠很好地減小TIME_WAIT套接字數量,可是對於 Squid,效果卻不大。此項參數能夠控制TIME_WAIT套接字的最大數量,避免Squid服務器被大量的TIME_WAIT套接字拖死。