1. 程式人生 > 其它 >伺服器的TIME_WAIT和CLOSE_WAIT

伺服器的TIME_WAIT和CLOSE_WAIT

1.1. 伺服器的TIME_WAIT和CLOSE_WAIT

在伺服器的日常維護過程中,排查TIME_WAIT和CLOSE_WAIT問題需要用到下面的命令:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

TIME_WAIT 814
CLOSE_WAIT 1
FIN_WAIT1 1
ESTABLISHED 634
SYN_RECV 2
LAST_ACK 1

常用的三個狀態是:ESTABLISHED 表示正在通訊,TIME_WAIT 表示主動關閉,CLOSE_WAIT 表示被動關閉。

檢視網路狀態,如果出現以下兩種問題,伺服器一般都出了異常。

  • 伺服器保持了大量TIME_WAIT狀態
  • 伺服器保持了大量CLOSE_WAIT狀態

因為linux分配給一個使用者的檔案控制代碼是有限的,而TIME_WAIT和CLOSE_WAIT兩種狀態如果一直被保持,那麼意味著對應數目的通道就一直被佔著,一旦達到控制代碼數上限,新的請求就無法被處理了,接著就是大量Too Many Open Files異常,最終導致tomcat崩潰。

1.1.1. 什麼是TIME-WAIT和CLOSE-WAIT ?

由於socket是全雙工的工作模式,一個socket的關閉,是需要四次握手來完成的:

  1. 主動關閉連線的一方,呼叫close();協議層傳送FIN包 ;
  2. 被動關閉的一方收到FIN包後,協議層回覆ACK;然後被動關閉的一方,進入CLOSE_WAIT狀態,主動關閉的一方等待對方關閉,則進入FIN_WAIT_2狀態;此時,主動關閉的一方等待被動關閉一方的應用程式,呼叫close操作 ;
  3. 被動關閉的一方在完成所有資料傳送後,呼叫close()操作;此時,協議層傳送FIN包給主動關閉的一方,等待對方的ACK,被動關閉的一方進入LAST_ACK狀態;
  4. 主動關閉的一方收到FIN包,協議層回覆ACK;此時,主動關閉連線的一方,進入TIME_WAIT狀態;而被動關閉的一方,進入CLOSED狀態 ;
  5. 等待2MSL時間,主動關閉的一方,結束TIME_WAIT,進入CLOSED狀態 ;

通過上面的一次socket關閉操作,可以得出以下幾點:

  1. 主動關閉連線的一方 – 也就是主動呼叫socket的close操作的一方,最終會進入TIME_WAIT狀態 ;
  2. 被動關閉連線的一方,有一箇中間狀態,即CLOSE_WAIT,因為協議層在等待上層的應用程式,主動呼叫close操作後才主動關閉這條連線 ;
  3. TIME_WAIT會預設等待2MSL時間後,才最終進入CLOSED狀態;
  4. 在一個連線沒有進入CLOSED狀態之前,這個連線是不能被重用的

TIME_WAIT並不可怕,CLOSE_WAIT才可怕,因為CLOSE_WAIT很多,表示說要麼是你的應用程式寫的有問題,沒有合適的關閉socket。要麼是伺服器CPU處理不過來(CPU太忙)或者你的應用程式一直睡眠到其它地方(鎖,或者檔案I/O等等),你的應用程式獲得不到合適的排程時間,造成你的程式沒法真正的執行close操作。

1.1.2. 處理方法

伺服器保持了大量TIME_WAIT狀態

這種情況比較常見,一些爬蟲伺服器或者WEB伺服器上經常會遇到這個問題。

TIME_WAIT是主動關閉連線的一方保持的狀態,對於爬蟲伺服器來說他本身就是“客戶端”,在完成一個任務之後,他就會發起主動關閉連線,從而進入TIME_WAIT的狀態,然後在保持這個狀態2MSL(max segment lifetime)時間之後,徹底關閉回收資源。

這樣做的主要出於以下兩個方面的考慮:

  • 防止上一次連線中的包,迷路後重新出現,影響新連線(經過2MSL,上一次連線中所有的重複包都會消失),可靠的關閉TCP連線。在主動關閉方傳送的最後一個 ack(fin) ,有可能丟失,這時被動方會重新發fin, 如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。另外這麼設計TIME_WAIT 會定時的回收資源,並不會佔用很大資源的,除非短時間內接受大量請求或者受到攻擊。
  • 基於TCP的HTTP協議,關閉TCP連線的是Server端,這樣,Server端會進入TIME_WAIT狀態,可 想而知,對於訪 問量大的Web Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓 240*1000=240,000個 TIME_WAIT的記錄,維護這些狀態給Server帶來負擔。當然現代作業系統都會用快速的查詢演算法來管理這些 TIME_WAIT,所以對於新的 TCP連線請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這麼多狀態要維護總是不好。

主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。另外這麼設計TIME_WAIT 會定時的回收資源,並不會佔用很大資源的,除非短時間內接受大量請求或者受到攻擊。

1.1.3. 解決方案

  • 通過修改/etc/sysctl.conf檔案,伺服器能夠快速回收和重用那些TIME_WAIT的資源。
#表示開啟SYN Cookies。當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉    
net.ipv4.tcp_syncookies = 1    
#表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉    
net.ipv4.tcp_tw_reuse = 1    
#表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉    
net.ipv4.tcp_tw_recycle = 1  
#表示如果套接字由本端要求關閉,這個引數決定了它保持在FIN-WAIT-2狀態的時間    
net.ipv4.tcp_fin_timeout=30

伺服器保持了大量的close_wait狀態

time_wait問題可以通過調整核心引數和適當的設定web伺服器的keep-Alive值來解決。因為time_wait是自己可控的,要麼就是對方連線的異常,要麼就是自己沒有快速的回收資源,總之不是由於自己程式錯誤引起的。但是close_wait就不一樣了,伺服器保持大量的close_wait只有一種情況,那就是對方傳送一個FIN後,程式自己這邊沒有進一步傳送ACK以確認。換句話說就是在對方關閉連線後,程式裡沒有檢測到,或者程式裡本身就已經忘了這個時候需要關閉連線,於是這個資源就一直被程式佔用著。

1.1.4. 解決方案

  • 關閉正在執行的程式,這個需要視業務情況而定。
  • 儘快地修改程式裡的bug,然後提交到線上伺服器。

微信打賞

支付寶打賞