摘要:服務(wù)器出現(xiàn)異常最長出現(xiàn)的狀況是服務(wù)器保持了大量的狀態(tài)。此時主動關(guān)閉一方必須保持一個有效的狀態(tài)下維持狀態(tài)信息,以便可以重發(fā)。這就意味著,一個成功建立的連接,必須使得之前網(wǎng)絡(luò)中殘余的數(shù)據(jù)報都丟失了。,維持這些狀態(tài)給服務(wù)器端帶來巨大的負(fù)擔(dān)。
查看TIME_WAIT和CLOSE_WAIT數(shù)的命令:
netstat -n | awk "/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}"
它會顯示例如下面的信息:
TIME_WAIT 、CLOSE_WAIT 、FIN_WAIT1 、ESTABLISHED 、SYN_RECV 、LAST_ACK
常用的三個狀態(tài)是:ESTABLISHED表示正在通信 、TIME_WAIT表示主動關(guān)閉、CLOSE_WAIT表示被動關(guān)閉。
服務(wù)器出現(xiàn)異常最長出現(xiàn)的狀況是:
服務(wù)器保持了大量的TIME_WAIT狀態(tài)。
服務(wù)器保持了大量的CLOSE_WAIT狀態(tài)。
我們也都知道Linux系統(tǒng)中分給每個用戶的文件句柄數(shù)是有限的,而TIME_WAIT和CLOSE_WAIT這兩種狀態(tài)如果一直被保持,那么意味著對應(yīng)數(shù)目的通道(此處應(yīng)理解為socket,一般一個socket會占用服務(wù)器端一個端口,服務(wù)器端的端口最大數(shù)是65535)一直被占用,一旦達到了上限,則新的請求就無法被處理,接著就是大量Too Many Open Files異常,然后tomcat、nginx、apache崩潰。。。
下面來討論這兩種狀態(tài)的處理方法,網(wǎng)絡(luò)上也有很多資料把這兩種情況混為一談,認(rèn)為優(yōu)化內(nèi)核參數(shù)就可以解決,其實這是不恰當(dāng)?shù)摹?yōu)化內(nèi)核參數(shù)在一定程度上能解決time_wait過多的問題,但是應(yīng)對close_wait還得從應(yīng)用程序本身出發(fā)。
服務(wù)器保持了大量的time_wait狀態(tài)
這種情況比較常見,一般會出現(xiàn)在爬蟲服務(wù)器和web服務(wù)器(如果沒做內(nèi)核參數(shù)優(yōu)化的話)上,那么這種問題是怎么產(chǎn)生的呢?
從上圖可以看出time_wait是主動關(guān)閉連接的一方保持的狀態(tài),對于爬蟲服務(wù)器來說它自身就是客戶端,在完成一個爬取任務(wù)后就會發(fā)起主動關(guān)閉連接,從而進入time_wait狀態(tài),然后保持這個狀態(tài)2MSL時間之后,徹底關(guān)閉回收資源。這里為什么會保持資源2MSL時間呢?這也是TCP/IP設(shè)計者規(guī)定的。
TCP要保證在所有可能的情況下使得所有的數(shù)據(jù)都能夠被正確送達。當(dāng)你關(guān)閉一個socket時,主動關(guān)閉一端的socket將進入TIME_WAIT狀 態(tài),而被動關(guān)閉一方則轉(zhuǎn)入CLOSED狀態(tài),這的確能夠保證所有的數(shù)據(jù)都被傳輸。當(dāng)一個socket關(guān)閉的時候,是通過兩端四次握手完成的,當(dāng)一端調(diào)用 close()時,就說明本端沒有數(shù)據(jù)要發(fā)送了。這好似看來在握手完成以后,socket就都可以處于初始的CLOSED狀態(tài)了,其實不然。原因是這樣安排狀態(tài)有兩個問題, 首先,我們沒有任何機制保證最后的一個ACK能夠正常傳輸,第二,網(wǎng)絡(luò)上仍然有可能有殘余的數(shù)據(jù)包(wandering duplicates),我們也必須能夠正常處理。
TIMEWAIT就是為了解決這兩個問題而生的。
假設(shè)最后的一個ACK丟失,那么被動關(guān)閉一方收不到這最后一個ACK則會重發(fā)FIN。此時主動關(guān)閉一方必須保持一個有效的(time_wait狀態(tài)下維持)狀態(tài)信息,以便可以重發(fā)ACK。如果主動關(guān)閉的socket不維持這種狀態(tài)而是進入close狀態(tài),那么主動關(guān)閉的一方在收到被動關(guān)閉方重新發(fā)送的FIN時則響應(yīng)給被動方一個RST。被動方收到這個RST后會認(rèn)為此次回話出錯了。所以如果TCP想要完成必要的操作而終止雙方的數(shù)據(jù)流傳輸,就必須完全正確的傳輸四次握手的四步,不能有任何的丟失。這就是為什么在socket在關(guān)閉后,任然處于time_wait狀態(tài)的第一個原因。因為他要等待可能出現(xiàn)的錯誤(被動關(guān)閉端沒有接收到最后一個ACK),以便重發(fā)ACK。
假設(shè)目前連接的通信雙方都調(diào)用了close(),雙方同時進入closed的終結(jié)狀態(tài),而沒有走 time_wait狀態(tài)。則會出現(xiàn)如下問題:假如現(xiàn)在有一個新的連接建立起來,使用的IP地址與之前的端口完全相同,現(xiàn)在建立的一個連接是之前連接的完全復(fù)用,我們還假定之前連接中有數(shù)據(jù)報殘存在網(wǎng)絡(luò)之中,這樣的話現(xiàn)在的連接收到的數(shù)據(jù)有可能是之前連接的報文。為了防止這一點。TCP不允許新的連接復(fù)用time_wait狀態(tài)下的socket。處于time_wait狀態(tài)的socket在等待2MSL時間后(之所以是兩倍的MSL,是由于MSL是一個數(shù)據(jù)報在網(wǎng)絡(luò)中單向發(fā)出 到認(rèn)定丟失的時間,即(Maximum Segment Lifetime)報文最長存活時間,一個數(shù)據(jù)報有可能在發(fā)送途中或是其響應(yīng)過程中成為殘余數(shù)據(jù)報,確認(rèn)一個數(shù)據(jù)報及其響應(yīng)的丟棄需要兩倍的MSL),將會轉(zhuǎn)為closed狀態(tài)。這就意味著,一個成功建立的連接,必須使得之前網(wǎng)絡(luò)中殘余的數(shù)據(jù)報都丟失了。
再引用網(wǎng)絡(luò)中的一段話:
值得一說的是,基于TCP的http協(xié)議,一般(此處為什么說一般呢,因為當(dāng)你在keepalive時間內(nèi) 主動關(guān)閉對服務(wù)器端的連接時,那么主動關(guān)閉端就是客戶端,否則客戶端就是被動關(guān)閉端。下面的爬蟲例子就是這種情況)主動關(guān)閉tcp一端的是server端,這樣server端就會進入time_wait狀態(tài),可想而知,對于訪問量大的web服務(wù)器,會存在大量的time_wait狀態(tài),假如server一秒鐘接收1000個請求,那么就會積壓240*1000=240000個time_wait狀態(tài)。(RFC 793中規(guī)定MSL為2分鐘,實際應(yīng)用中常用的是30秒,1分鐘和2分鐘等。),維持這些狀態(tài)給服務(wù)器端帶來巨大的負(fù)擔(dān)。當(dāng)然現(xiàn)代操作系統(tǒng)都會用快速的查找算法來管理這些 TIME_WAIT,所以對于新的 TCP連接請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這么多狀態(tài)要維護總是不好。
HTTP協(xié)議1.1版本規(guī)定default行為是keep-Alive,也就是會重用tcp連接傳輸多個 request/response。之所以這么做的主要原因是發(fā)現(xiàn)了我們上面說的這個問題。
服務(wù)器保持了大量的close_wait狀態(tài)
time_wait問題可以通過調(diào)整內(nèi)核參數(shù)和適當(dāng)?shù)脑O(shè)置web服務(wù)器的keep-Alive值來解決。因為time_wait是自己可控的,要么就是對方連接的異常,要么就是自己沒有快速的回收資源,總之不是由于自己程序錯誤引起的。但是close_wait就不一樣了,從上圖中我們可以看到服務(wù)器保持大量的close_wait只有一種情況,那就是對方發(fā)送一個FIN后,程序自己這邊沒有進一步發(fā)送ACK以確認(rèn)。換句話說就是在對方關(guān)閉連接后,程序里沒有檢測到,或者程序里本身就已經(jīng)忘了這個時候需要關(guān)閉連接,于是這個資源就一直被程序占用著。這個時候快速的解決方法是:
關(guān)閉正在運行的程序,這個需要視業(yè)務(wù)情況而定。
盡快的修改程序里的bug,然后測試提交到線上服務(wù)器。
注:
直到寫這篇文章的時候我才完全弄明白之前工作中遇到的一個問題。程序員寫了爬蟲(php)運行在采集服務(wù)器A上,程序去B服務(wù)器上采集資源,但是A服務(wù)器很快就發(fā)現(xiàn)出現(xiàn)了大量的close_wait狀態(tài)的連接。后來手動檢查才發(fā)現(xiàn)這些處于close_wait狀態(tài)的請求結(jié)果都是404,那就說明B服務(wù)器上沒有要請求的資源。
下面引用網(wǎng)友分析的結(jié)論:
服 務(wù)器A是一臺爬蟲服務(wù)器,它使用簡單的HttpClient去請求資源服務(wù)器B上面的apache獲取文件資源,正常情況下,如果請求成功,那么在抓取完 資源后,服務(wù)器A會主動發(fā)出關(guān)閉連接的請求,這個時候就是主動關(guān)閉連接,服務(wù)器A的連接狀態(tài)我們可以看到是TIME_WAIT。如果一旦發(fā)生異常呢?假設(shè) 請求的資源服務(wù)器B上并不存在,那么這個時候就會由服務(wù)器B發(fā)出關(guān)閉連接的請求,服務(wù)器A就是被動的關(guān)閉了連接,如果服務(wù)器A被動關(guān)閉連接之后程序員忘了 讓HttpClient釋放連接,那就會造成CLOSE_WAIT的狀態(tài)了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/39892.html
摘要:服務(wù)器出現(xiàn)異常最長出現(xiàn)的狀況是服務(wù)器保持了大量的狀態(tài)。此時主動關(guān)閉一方必須保持一個有效的狀態(tài)下維持狀態(tài)信息,以便可以重發(fā)。這就意味著,一個成功建立的連接,必須使得之前網(wǎng)絡(luò)中殘余的數(shù)據(jù)報都丟失了。,維持這些狀態(tài)給服務(wù)器端帶來巨大的負(fù)擔(dān)。 showImg(https://segmentfault.com/img/bV9DQk?w=732&h=563); showImg(https://se...
摘要:無法形容,直接對產(chǎn)生了滿分好感于是直接打開源碼目錄全局搜,找到,如下一段注釋掉了上面這些,跑起來沒有問題,這樣的問題就解決了。那么查一下,有說設(shè)置注冊表的感覺并不是解決辦法實測也不能解決問題。 背景 我司的軟件在一個客戶處測試功能和性能,這個客戶比較特殊: 他們客戶端是很舊的java代碼,且要求不能改動,客戶端的主要業(yè)務(wù)簡單說就是上傳下載文件 他們提供了客戶端demo,http請求是...
摘要:無法形容,直接對產(chǎn)生了滿分好感于是直接打開源碼目錄全局搜,找到,如下一段注釋掉了上面這些,跑起來沒有問題,這樣的問題就解決了。那么查一下,有說設(shè)置注冊表的感覺并不是解決辦法實測也不能解決問題。 背景 我司的軟件在一個客戶處測試功能和性能,這個客戶比較特殊: 他們客戶端是很舊的java代碼,且要求不能改動,客戶端的主要業(yè)務(wù)簡單說就是上傳下載文件 他們提供了客戶端demo,http請求是...
閱讀 2728·2021-11-25 09:43
閱讀 2684·2021-11-22 09:34
閱讀 2963·2021-11-12 10:34
閱讀 1509·2021-10-20 13:46
閱讀 2356·2019-08-30 13:21
閱讀 988·2019-08-30 11:21
閱讀 542·2019-08-30 11:20
閱讀 2247·2019-08-29 17:20