TCP之再談解決伺服器TIMEWAIT過多的問題

2021-06-20 01:22:49 字數 3662 閱讀 1828

這個問題在網上已經有很多人討論過了,再談這個問題,只是根據我處理過的相關業務來談談我的看法。至於什麼是timewait,我想,並不需要多說。

timewait狀態本身

和應用層的客戶端或者伺服器是沒有關係的。僅僅是主動關閉的一方,在使用fin|ack|fin|ack四分組正常關閉tcp連線的時候

timewait並不是多餘的

。在tcp協議被創造,

經歷了大量的實際場景實踐之後

,timewait

出現了,因為tcp主

動關閉連線的一方需要timewait狀態,它是我們的朋友。這是

《unix網路程式設計

》的作者----steven對timewait的態度。

tcp要保證在所有可能的情況下使得所有的資料都能夠被正確送達。當你關閉乙個

socket

時,主動關閉一端的

socket

將進入time_wait

狀態,而被動關閉一方則轉入closed狀態,這的確能夠保證所有的資料都被傳輸。當乙個socket關閉的時候,是通過兩端四次握手完成的,當一端呼叫close()時,就說明本端沒有資料要傳送了。這好似看來在握手完成以後,socket就都可以處於初始的closed狀態了,其實不然。原因是這樣安排狀態有兩個問題, 首先,我們沒有任何機制保證最後的乙個ack能夠正常傳輸,第二,網路上仍然有可能有殘餘的資料報(wandering duplicates),我們也必須能夠正常處理。

timewait就是為了解決這兩個問題而生的。

1.假設最後乙個

ack丟失了,被動關閉一方會重發它的

fin。

主動關閉一方必須維持乙個有效狀態資訊(timewait狀態下維持),以便能夠重發

ack。

如果主動關閉的socket不維持這種狀態而進入closed狀態,那麼主動關閉的socket在處於closed狀態時,

接收到fin後將會響應乙個rst。被動關閉一方接收到rst後會認為出錯了。如果tcp協議想要正常完成必要的操作而終止雙方的資料流傳輸,就必須完全正確的傳輸四次握手的四個節,不能有任何的丟失。這就是為什麼socket在關閉後,仍然處於time_wait狀態的第乙個原因,因為他要等待以便重發ack。

2.假設

目前連線的通訊雙方都已經呼叫了

close()

,雙方同時進入

closed的終結

狀態,而沒有走

time_wait

狀態。會出現如下問題,現在有乙個新的連線被建立起來,使用的ip位址與埠與先前的完全相同,後建立的連線是原先連線的乙個完全復用。還假定原先的連線中有資料報殘存於網路之中,這樣新的連線收到的資料報中有可能是先前連線的資料報。為了防止這一點,tcp不允許新連線復用time_wait狀態下的socket。處於time_wait狀態的socket在等待兩倍的msl時間以後(之所以是兩倍的msl,是由於msl是乙個資料報在網路中單向發出到認定丟失的時間,乙個資料報有可能在傳送途中或是其響應過程中成為殘餘資料報,確認乙個資料報及其響應的丟棄的需要兩倍的msl),將會轉變為closed狀態。這就意味著,乙個成功建立的連線,必然使得先前網路中殘餘的資料報都丟失了。

大量timewait出現,並且需要解決

的場景在高並發短連線的tcp伺服器上,當伺服器處理完

請求後立刻按照主動正常

關閉連線。。。這個場景下,

會出現大量socket處於

timewai

t狀態。如果客戶端的併發量持續很高,

此時部分客戶端就會顯示

連線不上。

我來解釋下這個場景。

主動正常關閉tcp連線,都會出

現timewait。為什麼我們要關注這個高並發短連線呢?有兩個方面需要注意

:1. 高併發可以讓伺服器在短時間範圍內同時

占用大量埠,而埠有個0~65535的範圍,並不是很多,刨除系統和其他服務要用的,剩下的就更少了

。2. 在這個場景中,短連線表示「業務處理+傳輸資料的時間 遠遠小於 timewait超時的時間」的連線。這裡有個相對長短

的概念,比如,取乙個web頁面,1秒鐘的http

短連線處理完業務,

在關閉連線之後,這個業務用過的埠

會停留在timewait狀態幾分鐘,而這幾分鐘,其他http請求來臨的時候是無法占用此埠的

。單用這個業務計算伺服器的利用率會發現,伺服器幹正經事的時間和埠(資源)

被掛著無法被使用的時間的比例是 1:幾百,伺服器資源嚴重浪費。(說個題外話,從這個意義出發來考慮伺服器效能調優的話,

長連線業務的服務

就不需要考慮timewait狀態。同時,假如你對伺服器業務場景非常熟悉,你會發現,在實際業務場景中,

一般長連線對應的業務的併發量並不會很高

)綜合這兩個方面,持續的到達一定量的

高並發短連線,會使伺服器因埠資源不足而拒絕為一

部分客戶服務。同時,這些埠都是伺服器臨時分配,無法用so_

reuse

addr選項解決這個問題:(

timewait既友好,又令人頭疼。

但是我們還是要抱著乙個友好的態度來看待它,因為它盡它的能力保證了伺服器的健壯性。

1. linux沒有在sysctl或者proc檔案系統暴露修改

這個timewait超時時間的介面

,可以修改核心協議棧**中關於這個timewait的超時時間引數,重編核心,讓它縮短超時時間,加快**;

2. 利用so_linger選項的強制關閉方式,發rst而不是fin,來越過timewait狀態,直接進入closed狀態。詳見我的博

文《tcp之選項

so_linger

》。為什麼說上述兩種解決方式我覺得可行,但是不符合原則?

我首先認為,我要依靠timewait狀態來保證我的伺服器程式健壯,網路上發生的亂七八糟的問題太多了,我先要服務功能正常。

那是不是就不要效能了呢?並不是。如果伺服器上跑的短連線業務量到了我真的必須處理這個timewait狀態過多的問題的時候,我的原則是盡量處理,而不是跟timewait幹上,非先除之而後快:)如果盡量處理了,還是解決不了問題,仍然拒絕服務

部分請求

,那我會採取分機器的方法

,讓多台機器來抗

這些高併發的短

真正地必須使用

上述我認為不合理的方式

來解決這個問題的場景有沒有呢?答案是有。

題外話,伺服器上的技術問題沒有絕對,一切都是為業務需求服務的。

sysctl改兩個核心引數就行了,如下:

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_tw_recycle = 1

簡單來說,就是開啟系統的timewait重用和快速**,至於怎麼重用和快速**,這個問題

我沒有深究,實際場景中這麼做確實有效果。

用netstat或者ss觀察就能得出結論。

還有些朋友同時也會開啟syncookies這個功能,如下:

net.ipv4.tcp_syncookies = 1

開啟這個syncookies

的目的實際上是:

「在伺服器資源(並非單指埠資源,拒絕服務有很多種資源不足的情況

)不足的

情況下,盡量不要拒絕tcp的

syn(連線

)請求,盡量把syn請求快取起來,留著過會兒有能力的時候

處理這些tcp的連線請求

」。如果併發量真的非常非常高,開啟這個其實用處不大。

swoole之TCP伺服器

建立server物件,監聽 127.0.0.1 9501埠 serv new swoole server 127.0.0.1 9501 serv set worker num 4,worker程序數 一般是cpu的倍數 max request 1000 最大請求數 監聽連線進入事件 fd 客戶端連線...

TCP伺服器模型

迴圈伺服器 迴圈伺服器在同乙個時刻只可以響應乙個客戶端的請求 併發伺服器 併發伺服器在同乙個時刻可以響應多個客戶端的請求 9.1 迴圈伺服器 udp伺服器 udp迴圈伺服器的實現非常簡單 udp伺服器每次從套接字上讀取乙個客戶端的請求,處理,然後將結果返回給客戶機.可以用下面的演算法來實現.sock...

tcp 伺服器優化

vi etc sysctl.conf 編輯檔案,加入以下內容 net.ipv4.tcp syncookies 1 net.ipv4.tcp tw reuse 1 net.ipv4.tcp tw recycle 1 net.ipv4.tcp fin timeout 30 然後執行 sbin sysct...