【轉】ubuntu中解決進程的time_wait和FIN_WAIT2狀態

 

原文:https://blog.csdn.net/wangshuminjava/article/details/107386053

 

https://ningg.top/computer-basic-theory-tcp-time-wait/

 

 

 

---------------------------------------

 

TIMEWAIT狀態本身和應用層的客戶端或者服務器是沒有關係的。如果你的程序設計爲服務器主動關閉,那麼你纔有可能需要關注這個TIMEWAIT狀態過多的問題。如果你的服務器設計爲被動關閉,那麼你首先要關注的是CLOSE_WAIT。
1
大量TIMEWAIT出現在業務上

在高併發短連接的TCP服務器上,當服務器處理完請求後立刻按照主動正常關閉連接。。。這個場景下,會出現大量socket處於TIMEWAIT狀態。如果客戶端的併發量持續很高,此時部分客戶端就會顯示連接不上。
1
業務上兩個方面需要注意:

1、高併發可以讓服務器在短時間範圍內同時佔用大量端口,而端口有個0~65535的範圍,並不是很多,刨除系統和其他服務要用的,剩下的就更少了。

2、在這個場景中,短連接表示“業務處理+傳輸數據的時間 遠遠小於 TIMEWAIT超時的時間”的連接。這裏有個相對長短的概念,比如,取一個web頁面,1秒鐘的http短連接處理完業務,在關閉連接之後,這個業務用過的端口會停留在TIMEWAIT狀態幾分鐘,而這幾分鐘,其他HTTP請求來臨的時候是無法佔用此端口的。單用這個業務計算服務器的利用率會發現,服務器幹正經事的時間和端口(資源)被掛着無法被使用的時間的比例是 1:幾百,服務器資源嚴重浪費。(說個題外話,從這個意義出發來考慮服務器性能調優的話,長連接業務的服務就不需要考慮TIMEWAIT狀態。同時,假如你對服務器業務場景非常熟悉,你會發現,在實際業務場景中,一般長連接對應的業務的併發量並不會很高)
1
2
3
綜合這兩個方面,持續的到達一定量的高併發短連接,會使服務器因端口資源不足而拒絕爲一部分客戶服務。同時,這些端口都是服務器臨時分配,無法用SO_REUSEADDR選項解決這個問題:(

解決進程的time_wait狀態

短時間後,所有的 TIME_WAIT 全都消失,被回收,端口包括服務,均正常。
Nginx 作爲反向代理時,大量的短鏈接,可能導致 Nginx 上的 TCP 連接處於 time_wait 狀態:

1、每一個 time_wait 狀態,都會佔用一個「本地端口」,上限爲 65535(16 bit,2 Byte);
2、當大量的連接處於 time_wait 時,新建立 TCP 連接會出錯,address already in use : connect
異常
即,在高併發的場景下,TIME_WAIT 連接存在,屬於正常現象。
線上場景中,持續的高併發場景

一部分 TIME_WAIT 連接被回收,但新的 TIME_WAIT 連接產生;
一些極端情況下,會出現大量的 TIME_WAIT 連接。
問題分析
大量的 TIME_WAIT 狀態 TCP 連接存在,其本質原因是什麼?

1、大量的短連接存在
2、特別是 HTTP 請求中,如果 connection 頭部取值被設置爲 close 時,基本都由「服務端」發起主動關閉連接
3、而,TCP 四次揮手關閉連接機制中,爲了保證 ACK 重發和丟棄延遲數據,設置 time_wait 爲 2 倍的 MSL(報文最大存活時間)
TIME_WAIT 狀態:

1、TCP 連接中,主動關閉連接的一方出現的狀態;(收到 FIN 命令,進入 TIME_WAIT 狀態,並返回 ACK 命令)
2、保持 2 個 MSL 時間,即,4 分鐘;(MSL 爲 2 分鐘)

思路 :

1、可以修改內核協議棧代碼中關於這個TIMEWAIT的超時時間參數,重編內核,讓它縮短超時時間,加快回收;
2、利用SO_LINGER選項的強制關閉方式,發RST而不是FIN,來越過TIMEWAIT狀態,直接進入CLOSED狀態。
1
2
查看進程連接狀態
統計 TCP 連接的狀態:

統計:各種連接的數量
#netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c
16 CLOSING
130 ESTABLISHED
298 FIN_WAIT1
13 FIN_WAIT2
9 LAST_ACK
7 LISTEN
103 SYN_RECV
5204 TIME_WAIT
#netstat -nat |grep TIME_WAIT

或者
統計:各種連接的數量
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 1154
TIME_WAIT 1645

查詢 TCP 連接狀態,其中 -E 表示 grep 或的匹配邏輯
$ netstat -nat | grep -E "TIME_WAIT|Local Address"
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 127.0.0.1.1080 127.0.0.1.59061 TIME_WAIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
狀態:描述

CLOSED: 無連接是活動的或正在進行
LISTEN: 服務器在等待進入呼叫
SYN_RECV: 一個連接請求已經到達,等待確認
SYN_SENT: 應用已經開始,打開一個連接
ESTABLISHED: 正常數據傳輸狀態
FIN_WAIT1: 應用說它已經完成
FIN_WAIT2: 另一邊已同意釋放
ITMED_WAIT: 等待所有分組死掉
CLOSING: 兩邊同時嘗試關閉
TIME_WAIT: 另一邊已初始化一個釋放
LAST_ACK: 等待所有分組死掉
1
2
3
4
5
6
7
8
9
10
11
如發現系統存在大量TIME_WAIT狀態的連接,通過調整內核參數解決,
vim /etc/sysctl.conf
netstat下time_wait狀態的tcp連接:

1.這是一種處於連接完全關閉狀態前的狀態;
2.通常要等上4分鐘(windows server)的時間才能完全關閉;
3.這種狀態下的tcp連接佔用句柄與端口等資源,服務器也要爲維護這些連接狀態消耗資源;
4.解決這種time_wait的tcp連接只有讓服務器能夠快速回收和重用那些TIME_WAIT的資源:修改註冊表[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters]添加dword值TcpTimedWaitDelay=30(30秒也爲微軟建議值;默認爲2分鐘)和MaxUserPort:65534(可選值5000 - 65534);
1
2
3
4
啓動程序發現端口被佔用,netstat查看之後發現如下現象:

發現端口處於TIME_WAIT狀態以及FIN_WAIT2狀態,無法釋放

TIME_WAIT狀態的來源 : 可能是之前虛擬機異常關機導致
TIME_WAIT狀態 產生原因: 只有首先調用close()發起主動關閉的一方纔會進入TIME_WAIT狀態,進入TIME_WAIT狀態的TCP連接需要經過2MSL才能回到初始狀態,
避免辦法 :儘量由客戶端主動關閉,避免服務端出現time_wait

根據一個查詢TCP連接數

netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'
或者
netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c
1
2
3


解決辦法
解決上述 time_wait 狀態大量存在,導致新連接創建失敗的問題,一般解決辦法:

1、客戶端, HTTP 請求的頭部,connection 設置爲 keep-alive,保持存活一段時間:現在的瀏覽器,一般都這麼進行了
2、服務器端,
允許 time_wait 狀態的 socket 被重用
縮減 time_wait 時間,設置爲 1 MSL(即,2 mins)
3、編輯內核文件/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 = 30 修改系默認的 TIMEOUT 時間
1
2
3
4
然後執行 /sbin/sysctl -p 讓參數生效.

/etc/sysctl.conf是一個允許改變正在運行中的Linux系統的接口,它包含一些TCP/IP堆棧和虛擬內存系統的高級選項,修改內核參數永久生效。
1
註釋 :

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 時間
1
2
3
4
簡單來說,就是打開系統的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套接字拖死。

net.ipv4.tcp_syncookies=1 打開TIME-WAIT套接字重用功能,對於存在大量連接的Web服務器非常有效。
net.ipv4.tcp_tw_recyle=1
net.ipv4.tcp_tw_reuse=1 減少處於FIN-WAIT-2連接狀態的時間,使系統可以處理更多的連接。
net.ipv4.tcp_fin_timeout=30 減少TCP KeepAlive連接偵測的時間,使系統可以處理更多的連接。
net.ipv4.tcp_keepalive_time=1800 增加TCP SYN隊列長度,使系統可以處理更多的併發連接。
net.ipv4.tcp_max_syn_backlog=8192
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
1
2
3
4
5
查看效果


解決進程的FIN_WAIT2狀態
FIN_WAIT_2狀態很多的原因: 服務端關閉,但客戶端沒有關閉。
解決:

1.服務端程序讀到 -1時,應該關閉連接,這樣服務端就會發起fin 指令。
進程關閉也會自動發fin 指令。

2. tcp協議裏沒有針對這個狀態設置超時時間,但linux服務器實現了超時回收,
1
2
3
4
tcp_fin_timeout ,默認是60秒, 可通過 /sbin/sysctl -a | grep timeout 查看

網絡超時:socket本身有 重連控制,網絡擁塞控制。

結論:幾個核心要點
1、time_wait 狀態的影響:
TCP 連接中,「主動發起關閉連接」 的一端,會進入 time_wait 狀態
time_wait 狀態,默認會持續 2 MSL(報文的最大生存時間),一般是 2x2 mins
time_wait 狀態下,TCP 連接佔用的端口,無法被再次使用
TCP 端口數量,上限是 6.5w(65535,16 bit)
大量 time_wait 狀態存在,會導致新建 TCP 連接會出錯,address already in use : connect 異常
2、現實場景:
1、服務器端,一般設置:不允許「主動關閉連接」
2、但 HTTP 請求中,http 頭部 connection 參數,可能設置爲 close,則,服務端處理完請求會主動關閉 TCP 連接
3、現在瀏覽器中, HTTP 請求 connection 參數,一般都設置爲 keep-alive
4、Nginx 反向代理場景中,可能出現大量短鏈接,服務器端,可能存在
3、解決辦法:
服務器端,
允許 time_wait 狀態的 socket 被重用
縮減 time_wait 時間,設置爲 1 MSL(即,2 mins)

FIN_WAIT1:
1、sysctl -a |grep tcp_max_orph(記下 net.ipv4.tcp_max_orphans 的值 第三步需要賦給orig_orphans)

2、sysctl -w net.ipv4.tcp_max_orphans=0 然後等待FIN_WAIT1的消失,可以用 netstat -np|grep 9080 反覆查看,直到沒有任何條目

3、sysctl -w net.ipv4.tcp_max_orphans=$orig_orphans

參考鏈接 :
解決TIME_WAIT過多造成的問題 : https://www.cnblogs.com/dadonggg/p/8778318.html
netstat -an查看到大量的TIME_WAIT狀態的解決辦法 :https://www.cnblogs.com/mobilecard/archive/2018/08/13/9468934.html

linux端口被佔用,netstat查看無進程號,端口狀態一直停留在FIN_WAIT1以及CLOSE_WAIT狀態
https://blog.csdn.net/microgp/article/details/86588973
http://blog.sina.com.cn/s/blog_77d329940102xa4t.html
FIN_WAIT_2 tcp狀態多原因剖析和解決 :https://blog.csdn.net/fei33423/article/details/50889385
基礎原理系列:服務端 TCP 連接的 TIME_WAIT 問題 :https://ningg.top/computer-basic-theory-tcp-time-wait/
————————————————
版權聲明:本文爲CSDN博主「王樹民」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wangshuminjava/article/details/107386053

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章