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