如何解决 linux socket TIME_WAIT 过多造成的问题(SYN、ACK、FIN、MSL、RST含义)netstat查看TCP连接数命令
文章目錄
- 解決方法1
- 疑問:tcp_tw_reuse如何打開?要編譯linux內核?
- 解決方法2:優化程序,減少TCP鏈接的創建與關閉,同一臺服務器,連接一次就好了,不要連接了又關閉,然后再連接
- 相應縮寫
- 一、TCP 狀態示意圖
- 二、查詢TCP連接數可以使用下面的Linux netstat命令
- 三、服務器出現異常最長出現的狀況是:
- 四、服務器保持了大量的time_wait狀態
- 五、服務器保持了大量的close_wait狀態
- 六、HttpClient未釋放連接,將會造成CLOSE_WAIT的狀態
- 參考文章
解決方法1
tcp_tw_reuse 表示開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認為0,表示關閉 tcp_tw_recycle 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉
對于客戶端
疑問:tcp_tw_reuse如何打開?要編譯linux內核?
tcp_tw_reuse可在客戶端打開,服務端不用打開
tcp_tw_reuse僅在TCP套接字作為客戶端,調用connect時起作用。絕大部分的TCP服務器,應該不會有大量主動連接的動作(或許會連接DB等,但一般也是長連接)。因此這個選項對于TCP服務來說,基本上是無用的,完全是沒必要打開
解決方法2:優化程序,減少TCP鏈接的創建與關閉,同一臺服務器,連接一次就好了,不要連接了又關閉,然后再連接
這邊直接轉載一篇文章吧:服務器TIME_WAIT和CLOSE_WAIT分析和解決辦法
相應縮寫
SYN:SYNchronize(同步)
ACK:ACKnowledge character(確認字符)
FIN:FINish(終止)
MSL:Maximum Segment Life(最大分段壽命)
RST:Reset(重置)
一、TCP 狀態示意圖
二、查詢TCP連接數可以使用下面的Linux netstat命令
netstat -ant|awk '/^tcp/ {++state[$NF]} END {for(key in state) print (key,state[key])}'我們hikflow_demo程序啟動后:(果然程序有問題啊,這么多TIME_WAIT的。。。)
(啟動后)
(過了一段時間)
# netstat -ant|awk '/^tcp/ {++state[$NF]} END {for(key in state) print (key,state[key])}' LISTEN 8 ESTABLISHED 4 CLOSE_WAIT 1 TIME_WAIT 111(關閉程序后)
# netstat -ant|awk '/^tcp/ {++state[$NF]} END {for(key in state) print (key,state[key])}' LISTEN 8 FIN_WAIT2 1 ESTABLISHED 3 TIME_WAIT 95(關閉程序后過一段時間,可以看到TIME_WAIT不斷減少,最終降為2)
# netstat -ant|awk '/^tcp/ {++state[$NF]} END {for(key in state) print (key,state[key])}' LISTEN 8 ESTABLISHED 4 TIME_WAIT 2常用的三個狀態是:ESTABLISHED表示正在通信 、TIME_WAIT表示主動關閉、CLOSE_WAIT表示被動關閉。
三、服務器出現異常最長出現的狀況是:
(1)服務器保持了大量的TIME_WAIT狀態。
(2)服務器保持了大量的CLOSE_WAIT狀態。
我們也都知道Linux系統中分給每個用戶的文件句柄數是有限的,而TIME_WAIT和CLOSE_WAIT這兩種狀態如果一直被保持,那么意味著對應數目的通道(此處應理解為socket,一般一個socket會占用服務器端一個端口,服務器端的端口最大數是65535)一直被占用,一旦達到了上限,則新的請求就無法被處理,接著就是大量Too Many Open Files異常,然后tomcat、nginx、apache崩潰。。。
下面來討論這兩種狀態的處理方法,網絡上也有很多資料把這兩種情況混為一談,認為優化內核參數就可以解決,其實這是不恰當的。優化內核參數在一定程度上能解決time_wait過多的問題,但是應對close_wait還得從應用程序本身出發。
四、服務器保持了大量的time_wait狀態
這種情況比較常見,一般會出現在爬蟲服務器和web服務器(如果沒做內核參數優化的話)上,那么這種問題是怎么產生的呢?
從上圖可以看出time_wait是主動關閉連接的一方保持的狀態,對于爬蟲服務器來說它自身就是客戶端,在完成一個爬取任務后就會發起主動關閉連接,從而進入time_wait狀態,然后保持這個狀態2MSL時間之后,徹底關閉回收資源。這里為什么會保持資源2MSL時間呢?這也是TCP/IP設計者規定的。
TCP要保證在所有可能的情況下使得所有的數據都能夠被正確送達。當你關閉一個socket時,主動關閉一端的socket將進入TIME_WAIT狀 態,而被動關閉一方則轉入CLOSED狀態,這的確能夠保證所有的數據都被傳輸。當一個socket關閉的時候,是通過兩端四次握手完成的,當一端調用 close()時,就說明本端沒有數據要發送了。這好似看來在握手完成以后,socket就都可以處于初始的CLOSED狀態了,其實不然。原因是這樣安排狀態有兩個問題, 首先,我們沒有任何機制保證最后的一個ACK能夠正常傳輸,第二,網絡上仍然有可能有殘余的數據包(wandering duplicates),我們也必須能夠正常處理。
TIME_WAIT就是為了解決這兩個問題而生的。
假設最后的一個ACK丟失,那么被動關閉一方收不到這最后一個ACK則會重發FIN。此時主動關閉一方必須保持一個有效的(time_wait狀態下維持)狀態信息,以便可以重發ACK。如果主動關閉的socket不維持這種狀態而是進入close狀態,那么主動關閉的一方在收到被動關閉方重新發送的FIN時則響應給被動方一個RST。被動方收到這個RST后會認為此次回話出錯了。所以如果TCP想要完成必要的操作而終止雙方的數據流傳輸,就必須完全正確的傳輸四次握手的四步,不能有任何的丟失。這就是為什么在socket在關閉后,任然處于time_wait狀態的第一個原因。因為他要等待可能出現的錯誤(被動關閉端沒有接收到最后一個ACK),以便重發ACK。
假設目前連接的通信雙方都調用了close(),雙方同時進入closed的終結狀態,而沒有走 time_wait狀態。則會出現如下問題:假如現在有一個新的連接建立起來,使用的IP地址與之前的端口完全相同,現在建立的一個連接是之前連接的完全復用,我們還假定之前連接中有數據報殘存在網絡之中,這樣的話現在的連接收到的數據有可能是之前連接的報文。為了防止這一點。TCP不允許新的連接復用time_wait狀態下的socket。處于time_wait狀態的socket在等待2MSL時間后(之所以是兩倍的MSL,是由于MSL是一個數據報在網絡中單向發出 到認定丟失的時間,即(Maximum Segment Lifetime)報文最長存活時間,一個數據報有可能在發送途中或是其響應過程中成為殘余數據報,確認一個數據報及其響應的丟棄需要兩倍的MSL),將會轉為closed狀態。這就意味著,一個成功建立的連接,必須使得之前網絡中殘余的數據報都丟失了。
再引用網絡中的一段話:
值得一說的是,基于TCP的http協議,一般(此處為什么說一般呢,因為當你在keepalive時間內 主動關閉對服務器端的連接時,那么主動關閉端就是客戶端,否則客戶端就是被動關閉端。下面的爬蟲例子就是這種情況)主動關閉tcp一端的是server端,這樣server端就會進入time_wait狀態,可想而知,對于訪問量大的web服務器,會存在大量的time_wait狀態,假如server一秒鐘接收1000個請求,那么就會積壓240*1000=240000個time_wait狀態。(RFC 793中規定MSL為2分鐘,實際應用中常用的是30秒,1分鐘和2分鐘等。),維持這些狀態給服務器端帶來巨大的負擔。當然現代操作系統都會用快速的查找算法來管理這些 TIME_WAIT,所以對于新的 TCP連接請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這么多狀態要維護總是不好。
HTTP協議1.1版本規定default行為是keep-Alive,也就是會重用tcp連接傳輸多個 request/response。之所以這么做的主要原因是發現了我們上面說的這個問題。
五、服務器保持了大量的close_wait狀態
time_wait問題可以通過調整內核參數和適當的設置web服務器的keep-Alive值來解決。因為time_wait是自己可控的,要么就是對方連接的異常,要么就是自己沒有快速的回收資源,總之不是由于自己程序錯誤引起的。但是close_wait就不一樣了,從上圖中我們可以看到服務器保持大量的close_wait只有一種情況,那就是對方發送一個FIN后,程序自己這邊沒有進一步發送ACK以確認。換句話說就是在對方關閉連接后,程序里沒有檢測到,或者程序里本身就已經忘了這個時候需要關閉連接,于是這個資源就一直被程序占用著。這個時候快速的解決方法是:
(1)關閉正在運行的程序,這個需要視業務情況而定。
(2)盡快的修改程序里的bug,然后測試提交到線上服務器。
六、HttpClient未釋放連接,將會造成CLOSE_WAIT的狀態
某爬蟲程序運行在采集服務器A上,然后去B服務器上采集資源,但是A服務器很快就發現出現了大量的close_wait狀態的連接。后來手動檢查才發現這些處于close_wait狀態的請求結果都是404,那就說明B服務器上沒有要請求的資源。
之所以出現close_wait,是因為:
服務器A是一臺爬蟲服務器,它使用簡單的HttpClient去請求資源服務器B上面的apache獲取文件資源,正常情況下,如果請求成功,那么在抓取完資源后,服務器A會主動發出關閉連接的請求,這個時候就是主動關閉連接,服務器A的連接狀態我們可以看到是TIME_WAIT。如果一旦發生異常呢?假設請求的資源服務器B上并不存在,那么這個時候就會由服務器B發出關閉連接的請求,服務器A就是被動的關閉了連接,如果服務器A被動關閉連接之后程序員忘了讓HttpClient釋放連接,那就會造成CLOSE_WAIT的狀態了。
參考文章
參考文章1:解決TIME_WAIT過多造成的問題
參考文章2:netstat命令 stat狀態說明
參考文章3:怎么解決socket短鏈接存在大量TIME_WAIT問題? - 知乎
參考文章4:服務器TIME_WAIT和CLOSE_WAIT分析和解決辦法
總結
以上是生活随笔為你收集整理的如何解决 linux socket TIME_WAIT 过多造成的问题(SYN、ACK、FIN、MSL、RST含义)netstat查看TCP连接数命令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux socket 流模式(STR
- 下一篇: socket通信流程图