TCP time_wait close_wait問題(多是全網最清楚的例子)

背景

公司羣裏,運維發現一個問題,task服務報錯(以下)python

The stream or file \"/data/logs/adn_task/offer_service.log\" could not be opened:
failed to open stream: Too many open files

測試老大看到了,根據經驗就推測是應該是文件句柄使用完了,應該有TCP鏈接不少沒釋放,果然發現是不少CLOSE_WAIT的狀態linux

簡單認知

短連接,一次連接就會佔用一個端口,一個端口就是一個文件描述符;
文件描述符 又稱 句柄,linux系統最大的句柄數是65535,能夠經過ulimit -a 查看
image.pngredis

三次握手

TCP創建鏈接須要通過三次握手;
通俗版本:
A: 你好,你能聽見我說話嗎?
B: 能聽到,你能聽到我說話嗎?
A:我也能聽到,咱們開始通訊吧shell

專業版本:
創建TCP鏈接時,須要客戶端和服務器共發送3個包。服務器

  • 第一次:客戶端發送初始序號x和syn=1請求標誌
  • 第二次:服務器發送請求標誌syn,發送確認標誌ACK,發送本身的序號seq=y,發送客戶端的確認序號ack=x+1
  • 第三次:客戶端發送ACK確認號,發送本身的序號seq=x+1,發送對方的確認號ack=y+1

四次揮手

TCP鏈接斷開須要通過四次揮手;
通俗版本:
前提A和B在通話
A:好的,個人話就說完了(FIN);
B:哦哦,我知道你說完啦(ACK),我還有說兩句哈;A: (沒說話,一直聽着)
B:哦了,我也說完了(FIN)
A:好的,我也知道你說玩了(ACK),掛電話吧網絡

專業版本:運維

  • 第一次揮手:客戶端發出釋放FIN=1,本身序列號seq=u,進入FIN-WAIT-1狀態
  • 第二次揮手:服務器收到客戶端的後,發出ACK=1確認標誌和客戶端的確認號ack=u+1,本身的序列號seq=v,進入CLOSE-WAIT狀態
  • 第三次揮手:客戶端收到服務器確認結果後,進入FIN-WAIT-2狀態。此時服務器發送釋放FIN=1信號,確認標誌ACK=1,確認序號ack=u+1,本身序號seq=w,服務器進入LAST-ACK(最後確認態)
  • 第四次揮手:客戶端收到回覆後,發送確認ACK=1,ack=w+1,本身的seq=u+1,客戶端進入TIME-WAIT(時間等待)。客戶端通過2個最長報文段壽命後,客戶端CLOSE;服務器收到確認後,馬上進入CLOSE狀態。

狀態流轉圖

實際例子

創建鏈接

linux上起了一個redis服務
image.png
本地起的6379端口tcp

仍是同一臺機器上,經過python腳本鏈接該redis服務:
image.png測試

此時網絡鏈接以下:
image.png
關注這兩個網絡鏈接,第一個是redis-server的,第二是python腳本的,此時都是ESTABLISHED狀態,表示這兩個進程創建了鏈接優化

TIME_WAIT狀況

如今斷掉python
image.png
以前的python的那個鏈接,是TIME_WAIT狀態
客戶端(主動方)主動斷開,進入TIME_WAIT狀態,服務端(被動方)進去CLOSE狀態,就是沒有顯示了

等待2MSL(1分鐘)後,以下:
image.png
TIME_WAIT狀態的鏈接也消失了,TIME_WAIT回收機制,系統ing過一段時間會回收,資源重利用

CLOSE_WAIT狀況

先創建鏈接,以下:
image.png

關掉redis服務,service redis stop
image.png
以前的redis-server的45370端口鏈接 進入了FIN_WAIT2狀態,而python端(被動關閉方)就進去了CLOSE_WAIT狀態

等待30s後,在看鏈接
image.png
只有python的那條CLOSE_WAIT

再次操做python端的腳本,再次get
image.png

關於6379端口(redis端口)的網絡鏈接都沒有了
image.png

TCP參數設置

如何快速回收TIME_WAIT和FIN_WAIT
/etc/sysctl.conf 包含如下配置項
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
root權限 執行/sbin/sysctl -p使之生效

經驗之談

我的經驗,不必定對,若有錯誤,請指正

  1. 當出現了CLOSE_WAIT大機率是業務代碼問題,代碼中沒有處理服務異常的狀況,如上面的例子,python再次請求redis的時候,發現redis掛了,就會主動幹掉CLOSE_WAIT狀態
  2. 出現大量TIME_WAIT的狀況,通常是服務端沒有及時回收端口,linux內核參數須要調整優化

參考資料

https://www.mobibrw.com/2019/20477

相關文章
相關標籤/搜索