TIME_WAIT
客戶端與服務器端建立TCP/IP連接后關閉SOCKET后,服務器端連接的端口狀態為TIME_WAIT.主動關閉的一方在發送最后一個 ack 后
就會進入 TIME_WAIT 狀態 停留2MSL(max segment lifetime)時間
這個是TCP/IP必不可少的,也就是“解決”不了的,也就是TCP/IP設計者本來是這么設計的
主要有兩個原因
1。防止上一次連接中的包,迷路后重新出現,影響新連接
(經過2MSL,上一次連接中所有的重復包都會消失)
2。可靠的關閉TCP連接
在主動關閉方發送的最后一個 ack(fin) ,有可能丟失,這時被動方會重新發fin, 如果這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。所以主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。
TIME_WAIT 並不會占用很大資源的,除非受到攻擊。
還有,如果一方 send 或 recv 超時,就會直接進入 CLOSED 狀態
1
netstat -an
查看下,發現系統中有很多time_wait的連接。因此直接用一下命令查看詳細情況
1
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
具體的解決方式
vim /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 時間
/sbin/sysctl -p //保存后生效
目前看來最好的辦法是讓每個TIME_WAIT早點過期。
在linux上可以這么配置:
#讓TIME_WAIT狀態可以重用,這樣即使TIME_WAIT占滿了所有端口,也不會拒絕新的請求造成障礙
echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
#讓TIME_WAIT盡快回收,我也不知是多久,觀察大概是一秒鍾
echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle
很多文檔都會建議兩個參數都配置上,但是我發現只用修改tcp_tw_recycle就可以解決問題的了,TIME_WAIT重用TCP協議本身就是不建議打開的。
不能重用端口可能會造成系統的某些服務無法啟動,比如要重啟一個系統監控的軟件,它用了40000端口,而這個端口在軟件重啟過程中剛好被使用了,就可能會重啟失敗的。linux默認考慮到了這個問題,有這么個設定:
#查看系統本地可用端口極限值
cat /proc/sys/net/ipv4/ip_local_port_range
用這條命令會返回兩個數字,默認是:32768 61000,說明這台機器本地能向外連接61000-32768=28232個連接,注意是本地向外連接,不是這台機器的所有連接,不會影響這台機器的80端口的對外連接數。但這個數字會影響到代理服務器(nginx)對app服務器的最大連接數,因為nginx對app是用的異步傳輸,所以這個環節的連接速度很快,所以堆積的連接就很少。假如nginx對app服務器之間的帶寬出了問題或是app服務器有問題,那么可能使連接堆積起來,這時可以通過設定nginx的代理超時時間,來使連接盡快釋放掉,一般來說極少能用到28232個連接。