通過netstat -anp | grep postgresport可以發現端口處于TIME_WAIT狀态,是以這裡就是要解決TIME_WAIT過多造成的問題。

time_wait的作用
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造成的影響
在高并發短連接配接的TCP伺服器上,當伺服器處理完請求後立刻主動正常關閉連接配接。這個場景下會出現大量socket處于TIME_WAIT狀态。如果用戶端的并發量持續很高,此時部分用戶端就會顯示連接配接不上。我來解釋下這個場景。主動正常關閉TCP連接配接,都會出現TIMEWAIT。為什麼我們要關注這個高并發短連接配接呢?有兩個方面需要注意:
1.高并發可以讓伺服器在短時間範圍内同時占用大量端口,而端口有個0~65535的範圍,并不是很多,刨除系統和其他服務要用的,剩下的就更少了。
2.在這個場景中,短連接配接表示“業務處理+傳輸資料的時間 遠遠小于 TIMEWAIT逾時的時間”的連接配接。
這裡有個相對長短的概念,比如取一個web頁面,1秒鐘的http短連接配接處理完業務,在關閉連接配接之後,這個業務用過的端口會停留在TIMEWAIT狀态幾分鐘,而這幾分鐘,其他HTTP請求來臨的時候是無法占用此端口的(占着茅坑不拉翔)。單用這個業務計算伺服器的使用率會發現,伺服器幹正經事的時間和端口(資源)被挂着無法被使用的時間的比例是 1:幾百,伺服器資源嚴重浪費。(說個題外話,從這個意義出發來考慮伺服器性能調優的話,長連接配接業務的服務就不需要考慮TIMEWAIT狀态。同時,假如你對伺服器業務場景非常熟悉,你會發現,在實際業務場景中,一般長連接配接對應的業務的并發量并不會很高。綜合這兩個方面,持續的到達一定量的高并發短連接配接,會使伺服器因端口資源不足而拒絕為一部分客戶服務。同時,這些端口都是伺服器臨時配置設定,無法用SO_REUSEADDR選項解決這個問題。
關于time_wait的反思:存在即是合理的,既然TCP協定能盛行四十多年,就證明他的設計合理性。是以我們盡可能的使用其原本功能。
依靠TIME_WAIT狀态來保證我的伺服器程式健壯,服務功能正常。那是不是就不要性能了呢?并不是。如果伺服器上跑的短連接配接業務量到了我真的必須處理這個TIMEWAIT狀态過多的問題的時候,我的原則是盡量處理,而不是跟TIMEWAIT幹上,非先除之而後快。如果盡量處理了,還是解決不了問題,仍然拒絕服務部分請求,那我會采取負載均衡來抗這些高并發的短請求。持續十萬并發的短連接配接請求,兩台機器,每台5萬個,應該夠用了吧。一般的業務量以及國内大部分網站其實并不需要關注這個問題,一句話,達不到時才需要關注這個問題的通路量。
小知識點:TCP協定發表:1974年12月,卡恩、瑟夫的第一份TCP協定詳細說明正式發表。當時美國國防部與三個科學家小組簽定了完成TCP/IP的協定,結果由瑟夫領銜的小組捷足先登,首先制定出了通過詳細定義的TCP/IP協定标準。當時作了一個試驗,将資訊包通過點對點的衛星網絡,再通過陸地電纜,再通過衛星網絡,再由地面傳輸,貫串歐洲和美國,經過各種電腦系統,全程9.4萬公裡竟然沒有丢失一個資料位,遠距離的可靠資料傳輸證明了TCP/IP協定的成功。
案列分析
首先,根據一個查詢TCP連接配接數,來說明這個問題。
netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'
LAST_ACK 14 # LAST_ACK:等待所有分組死掉
SYN_RECV 348 # SYN_RECV:一個連接配接請求已經到達,等待确認
ESTABLISHED 70 # ESTABLISHED:正常資料傳輸狀态
FIN_WAIT1 229 # FIN_WAIT1:應用說它已經完成
FIN_WAIT2 30 # FIN_WAIT2:另一邊已同意釋放
CLOSING 33 # CLOSING:兩邊同時嘗試關閉
TIME_WAIT 18122 # TIME_WAIT:另一邊已初始化一個釋放
# CLOSED:無連接配接是活動的或正在進行
# LISTEN:伺服器在等待進入呼叫
# ITMED_WAIT:等待所有分組死掉
# SYN_SENT:應用已經開始,打開一個連接配接
先來看看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過多?
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
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套接字将立刻被清除并列印警告資訊。