Time_wait 問題描述
在高并發(fā)的場景下驶冒,TIME_WAIT 連接存在殊轴,屬于正澈垦睿現(xiàn)象
線上場景中蓄喇,持續(xù)的高并發(fā)場景
一部分 TIME_WAIT 連接被回收发侵,但新的 TIME_WAIT 連接產(chǎn)生;
一些極端情況下公罕,會出現(xiàn)大量 的 TIME_WAIT 連接器紧。
Nginx 作為反向代理時,大量的短鏈接楼眷,可能導(dǎo)致 Nginx 上的 TCP 連接處于 time_wait 狀態(tài):
每一個 time_wait 狀態(tài)铲汪,都會占用一個「本地端口」,上限為 65535(16 bit罐柳,2 Byte)掌腰;
當(dāng)大量的連接處于 time_wait 時,新建立 TCP 連接會出錯张吉,address already in use : connect 異常
統(tǒng)計 TCP 連接的狀態(tài):
1. `// 統(tǒng)計:各種連接的數(shù)量`
2. `$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'`
3. `ESTABLISHED 1154`
4. `TIME_WAIT 1645`
問題分析
大量的 TIME_WAIT 狀態(tài) TCP 連接存在齿梁,其本質(zhì)原因是什么?
大量的短連接 存在
特別是 HTTP 請求中肮蛹,如果 connection 頭部取值被設(shè)置為 close 時勺择,基本都由「服務(wù)端 」發(fā)起主動關(guān)閉連接
而,TCP 四次揮手關(guān)閉連接機(jī)制中伦忠,為了保證 ACK 重發(fā)和丟棄延遲數(shù)據(jù)省核,設(shè)置 time_wait 為 2 倍的 MSL(報文最大存活時間)
TIME_WAIT 狀態(tài):
TCP 連接中,主動關(guān)閉連接 的一方出現(xiàn)的狀態(tài)昆码;(收到 FIN 命令气忠,進(jìn)入 TIME_WAIT 狀態(tài),并返回 ACK 命令)
保持 2 個 MSL 時間赋咽,即旧噪,4 分鐘;(MSL 為 2 分鐘)
解決辦法
解決上述 time_wait 狀態(tài)大量存在脓匿,導(dǎo)致新連接創(chuàng)建失敗的問題淘钟,一般解決辦法
- 客戶端:HTTP請求的頭部,connection設(shè)置為keep-alive陪毡,保持存活一段時間日月,現(xiàn)在的瀏覽器袱瓮,一般都這樣
- 服務(wù)端:
- 允許 time_wait狀態(tài)的socket被重用
- 縮減time_wait時間缤骨,設(shè)置為1MSL(即爱咬,2min)
結(jié)論
- TCP連接中,“主動發(fā)起關(guān)閉連接”的一端绊起,會進(jìn)入time_wait的狀態(tài)
- time_wait狀態(tài)精拟,默認(rèn)會持續(xù)2MSL(報文的最大生存時間),一般是2 * 2min
- time_wait狀態(tài)下虱歪,TCP連接占用的端口蜂绎,無法被再次使用,上線是65536
4.大量 time_wait 狀態(tài)存在笋鄙,會導(dǎo)致新建 TCP 連接會出錯师枣,address already in use : connect 異常
time_wait 狀態(tài)產(chǎn)生的原因
- 為實現(xiàn)TCP全雙工連接的可靠釋放(A->B: 為了保證主動關(guān)閉方 發(fā)送的最后一個ACK能到到對方)這個ACK報文段可能丟失,因而處于Last-ACK狀態(tài)的B收不到對已經(jīng)發(fā)送的FIN+ACK報文段的確認(rèn)萧落。 B會超時重傳這個FIN+ACK.践美。 如果A不在time-wait狀態(tài)等一段時間,而是發(fā)送完ACK后立即釋放連接找岖,那么就無法收到B重傳的FIN+ACK報文段陨倡。B無法正常closed
- 為使舊的數(shù)據(jù)包在網(wǎng)絡(luò)因過期而消失
A發(fā)送完最后一個ACK報文段后,再經(jīng)過2MSL后许布,就可以使本連接持續(xù)的時間內(nèi)所產(chǎn)生的所有報文段都從網(wǎng)絡(luò)中消失兴革。這樣下一個新的連接不會出現(xiàn)舊的數(shù)據(jù)包。 B結(jié)束TCP連接的時間比A早一些蜜唾。
大量TIME_WAIT造成的影響
為什么我們要關(guān)注這個高并發(fā)短連接呢杂曲?有兩個方面需要注意
高并發(fā)可以讓服務(wù)器在短時間范圍內(nèi)同時占用大量端口,而端口有個0~65535的范圍限制袁余,并不是很多擎勘,刨除系統(tǒng)和其他服務(wù)要用的,剩下的就更少了
在這個場景中泌霍,短連接表示“業(yè)務(wù)處理+傳輸數(shù)據(jù)的時間 遠(yuǎn)遠(yuǎn)小于 TIMEWAIT超時的時間”的連接货抄。單用這個業(yè)務(wù)計算服務(wù)器的利用率會發(fā)現(xiàn),服務(wù)器干正經(jīng)事的時間和端口(資源)被掛著無法被使用的時間的比例是 1:幾百朱转,服務(wù)器資源嚴(yán)重浪費
綜合這兩個方面蟹地,持續(xù)的到達(dá)一定量的高并發(fā)短連接,會使服務(wù)器因端口資源不足而拒絕為一部分客戶端服務(wù)藤为。同時怪与,這些端口都是服務(wù)器臨時分配,無法用SO_REUSEADDR選項解決這個問題
關(guān)于time_wait的反思:
存在即是合理的缅疟,既然TCP協(xié)議能盛行四十多年分别,就證明他的設(shè)計合理性遍愿。所以我們盡可能的使用其原本功能。
依靠TIME_WAIT狀態(tài)來保證我的服務(wù)器程序健壯耘斩,服務(wù)功能正常沼填。
那是不是就不要性能了呢?并不是括授。如果服務(wù)器上跑的短連接業(yè)務(wù)量到了我真的必須處理這個TIMEWAIT狀態(tài)過多的問題的時候坞笙,我的原則是盡量處理,而不是跟TIMEWAIT干上荚虚,非先除之而后快薛夜。
如果盡量處理了,還是解決不了問題版述,仍然拒絕服務(wù)部分請求梯澜,那我會采取負(fù)載均衡來抗這些高并發(fā)的短請求。持續(xù)十萬并發(fā)的短連接請求渴析,兩臺機(jī)器晚伙,每臺5萬個,應(yīng)該夠用了吧檬某。一般的業(yè)務(wù)量以及國內(nèi)大部分網(wǎng)站其實并不需要關(guān)注這個問題撬腾,一句話,達(dá)不到時才需要關(guān)注這個問題的訪問量恢恼。
附錄: TCP 三次握手和四次握手
1.三次握手 民傻,建立連接過程
2.四次揮手,釋放連接過程
幾個核心疑問:
1场斑、 time_wait 是「服務(wù)器端」的狀態(tài)漓踢?or 「客戶端」的狀態(tài)?
RE:time_wait 是「主動關(guān)閉 TCP 連接」一方的狀態(tài)漏隐,可能是「客服端」的喧半,也可能是「服務(wù)器端」的
一般情況下,都是「客戶端」所處的狀態(tài)青责;「服務(wù)器端」一般設(shè)置「不主動關(guān)閉連接」
2挺据、 服務(wù)器在對外服務(wù)時,是「客戶端」發(fā)起的斷開連接脖隶?還是「服務(wù)器」發(fā)起的斷開連接扁耐?
正常情況下,都是「客戶端」發(fā)起的斷開連接
「服務(wù)器」一般設(shè)置為「不主動關(guān)閉連接」产阱,服務(wù)器通常執(zhí)行「被動關(guān)閉」
但 HTTP 請求中婉称,http 頭部 connection 參數(shù),可能設(shè)置為 close,則王暗,服務(wù)端處理完請求會主動關(guān)閉 TCP 連接
關(guān)于 Apache httpd 服務(wù)器的關(guān)聯(lián)配置悔据,參考:https://elf8848.iteye.com/blog/1739571
關(guān)于 HTTP 請求中,設(shè)置的主動關(guān)閉 TCP 連接的機(jī)制:TIME_WAIT的是主動斷開方才會出現(xiàn)的俗壹,所以主動斷開方是服務(wù)端科汗?
答案是是的。在HTTP1.1協(xié)議中策肝,有個 Connection 頭肛捍,Connection有兩個值,close和keep-alive之众,這個頭就相當(dāng)于客戶端告訴服務(wù)端,服務(wù)端你執(zhí)行完成請求之后依许,是關(guān)閉連接還是保持連接棺禾,保持連接就意味著在保持連接期間,只能由客戶端主動斷開連接峭跳。還有一個keep-alive的頭膘婶,設(shè)置的值就代表了服務(wù)端保持連接保持多久。
HTTP默認(rèn)的Connection值為close蛀醉,那么就意味著關(guān)閉請求的一方幾乎都會是由服務(wù)端這邊發(fā)起的悬襟。那么這個服務(wù)端產(chǎn)生TIME_WAIT過多的情況就很正常了。
雖然HTTP默認(rèn)Connection值為close拯刁,但是脊岳,現(xiàn)在的瀏覽器發(fā)送請求的時候一般都會設(shè)置Connection為keep-alive了确镊。所以灌具,也有人說,現(xiàn)在沒有必要通過調(diào)整參數(shù)來使TIME_WAIT降低了璧榄。
關(guān)于 time_wait :
1帚桩、TCP 連接建立后亿驾,「主動關(guān)閉連接 」的一端,收到對方的 FIN 請求后账嚎,發(fā)送 ACK 響應(yīng)莫瞬,會處于 time_wait 狀態(tài);
2郭蕉、 time_wait 狀態(tài) 疼邀,存在的必要性:
可靠的實現(xiàn) TCP 全雙工連接的終止 :四次揮手關(guān)閉 TCP 連接過程中,最后的 ACK 是由「主動關(guān)閉連接」的一端發(fā)出的恳不,如果這個 ACK 丟失檩小,則,對方會重發(fā) FIN 請求烟勋,因此规求,在「主動關(guān)閉連接」的一段筐付,需要維護(hù)一個 time_wait 狀態(tài),處理對方重發(fā)的 FIN 請求阻肿;
處理延遲到達(dá)的報文 :由于路由器可能抖動瓦戚,TCP 報文會延遲到達(dá),為了避免「延遲到達(dá)的 TCP 報文」被誤認(rèn)為是「新 TCP 連接」的數(shù)據(jù)丛塌,則较解,需要在允許新創(chuàng)建 TCP 連接之前,保持一個不可用的狀態(tài)赴邻,等待所有延遲報文的消失印衔,一般設(shè)置為 2 倍的 MSL(報文的最大生存時間),解決「延遲達(dá)到的 TCP 報文」問題