www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]本文是以安琪拉在項目中解決的一個網(wǎng)絡(luò)異常(Broken Pipe)為出發(fā)點寫的,我想玩家們應(yīng)該多少遇到過網(wǎng)絡(luò)問題,這篇文章也提供了一種解決網(wǎng)絡(luò)問題的思路,遇到類似網(wǎng)絡(luò)問題時可以借鑒這個思路。 為了盡量講的清晰明了,以下面這個流程進(jìn)行: 背景:安琪拉遇到網(wǎng)

本文是以安琪拉在項目中解決的一個網(wǎng)絡(luò)異常(Broken Pipe)為出發(fā)點寫的,我想玩家們應(yīng)該多少遇到過網(wǎng)絡(luò)問題,這篇文章也提供了一種解決網(wǎng)絡(luò)問題的思路,遇到類似網(wǎng)絡(luò)問題時可以借鑒這個思路。

為了盡量講的清晰明了,以下面這個流程進(jìn)行:

  1. 背景:安琪拉遇到網(wǎng)絡(luò)異常

  2. 小試牛刀:在Wireshark數(shù)據(jù)包中看三次握手

  3. 預(yù)備知識:回顧網(wǎng)絡(luò)分層、三次握手、四次揮手等網(wǎng)絡(luò)基礎(chǔ)知識

  4. 異常定位:Wireshark 分析定位Broken Pipe 異常原因

  5. 代碼修復(fù):調(diào)整客戶端代碼

  6. 擴(kuò)展閱讀:其他網(wǎng)絡(luò)知識及面試題

1. 背景:安琪拉遇到網(wǎng)絡(luò)異常

1. 異常信息

安琪拉做的一部分工作是和公司外部機構(gòu)對接系統(tǒng),對接方式很多種,有的機構(gòu)提供封裝好的SDK,有的提供統(tǒng)一網(wǎng)關(guān)平臺、還有一個接口一個HTTP URL等。涉及到的應(yīng)用層協(xié)議主要有https、http、sftp、ftp等。最近對接的一家機構(gòu)生產(chǎn)環(huán)境聯(lián)調(diào)日志出現(xiàn)了網(wǎng)絡(luò)異常,異常棧如下:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
網(wǎng)絡(luò)異常棧

第一眼看異常棧直譯過來就是管道破裂,為什么會出現(xiàn)管道破裂呢?這篇文章就由此而來,背后牽涉的是我們常常掛在嘴邊,面試也經(jīng)常被問的httptcp 協(xié)議的知識,這里埋個伏筆,后面我們由淺入深,慢慢把這個問題解決,同時了解如何運用網(wǎng)絡(luò)知識解決實際網(wǎng)絡(luò)問題。

安琪拉遇到異常是很興奮的,又可以學(xué)知識了,咳咳哈!當(dāng)然咯,前提是不影響到正常業(yè)務(wù)。這里現(xiàn)象是每個http 請求發(fā)起總是失敗,請求重試大概率會成功。

閑扯一段:其實一般應(yīng)用本地的異常還是比較好排查的,像空指針、內(nèi)存溢出、數(shù)組越界等都比較好搞!網(wǎng)絡(luò)問題,尤其是跟外部公網(wǎng)打交道的網(wǎng)絡(luò)問題有時間還是會稍微棘手的,因為公網(wǎng)環(huán)境比較復(fù)雜,中間涉及的跳轉(zhuǎn)節(jié)點比較多,很考驗工程師網(wǎng)絡(luò)問題定位的水平。

安琪拉知道運維中有個詞叫根故障定位,實際生產(chǎn)中很多異常的根故障最后都會定位到網(wǎng)絡(luò)這一層。

想說點題外話,安琪拉工作這幾年的一點體會,工程師的能力分二種,有一種能力叫掌握知識的能力,也叫學(xué)習(xí)能力,另一種能力叫解決問題的能力,學(xué)生時代前一種能力很容易體現(xiàn),成績和分?jǐn)?shù),而工作以后,公司更關(guān)注或者說更看重的是解決問題的能力,因為企業(yè)招工程師最終還是為了創(chuàng)造業(yè)務(wù)價值,創(chuàng)造業(yè)務(wù)價值就需要不斷解決實際業(yè)務(wù)的一個一個問題,不論是新業(yè)務(wù)需求還是系統(tǒng)bug都可以看做是面臨的問題。當(dāng)然這二種能力不是完全割裂的,往往相輔相成,解決問題能力強的人學(xué)習(xí)能力不會弱。只是看個人更偏重哪一方面? 學(xué)習(xí)能力強的人適合做學(xué)術(shù)型人才,解決問題能力強的適合做工程型人才。

閑扯扯完了,舒服! 發(fā)現(xiàn)寫體會、感受比寫技術(shù)文章輕松流暢多了!技術(shù)需要嚴(yán)謹(jǐn)細(xì)致,體會感受就很個人,很感性的東西,對錯因人而異!

2. 網(wǎng)絡(luò)抓包

遇到上面Broken Pipe這個問題,不急,第一步企微上撩一下運維大哥,讓他幫我抓個包下來看下。

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
抓包聊天截圖

抓包命令如下:

1tcpdump tcp port 20004 and host  **.**.com -w brokenpipe.cap        

tcpdump : dump the traffic on a network,根據(jù)使用者的定義對網(wǎng)絡(luò)上的數(shù)據(jù)包進(jìn)行截獲的包分析工具。

大家也可以邊看邊自己抓包,常用的命令格式如下:

1tcpdump tcp port 端口號 and host 域名 -w  保存文件

如果網(wǎng)絡(luò)流量高,不要抓太久,不然包會很大,分析的時候加載慢!

tcpdump 完整命令可以用tcpdump --help 查看。

2. 小試牛刀:在Wireshark數(shù)據(jù)包中看三次握手

之前如果沒有用過Wireshark 做過網(wǎng)絡(luò)包分析的,安琪拉擔(dān)心直接上來就看包分析有點費勁,下面安琪拉會對wireshark 先做個簡單的介紹。

2.1 Wireshark 簡介

首先,把運維給的包存在本地, 打開wireshark 導(dǎo)入網(wǎng)絡(luò)包,開始有趣的網(wǎng)絡(luò)漫游之旅。

wireshark 是一款非常流行的網(wǎng)絡(luò)包分析工具,經(jīng)常是網(wǎng)絡(luò)工程師/后端工程師用來分析網(wǎng)絡(luò)包,解決網(wǎng)絡(luò)問題的利器。先放一張圖出來聞聞味:

安琪拉截了一張自己本機裝的 wireshark 軟件,分為四個部分:

  1. 頂部是菜單欄

  2. 淺綠色背景是過濾器,這個對于分析包很重要,后面會講

  3. 列表那一欄是包列表信息

  4. 最下面的這個是包詳細(xì)信息,包含各層協(xié)議的內(nèi)容(應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層、物理層)

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
Wireshark介紹

吐槽一下微信的截圖,文字的樣式都不能改,太難看!

2.2 Wireshark 過濾器

過濾器單獨拿出來說下,因為確實很有用,后面異常分析會用到。

你導(dǎo)入的包可能內(nèi)容很多,需要使用過濾器篩選一下,過濾器很多種過濾的規(guī)則,我列舉一下常用的幾種:

  • 協(xié)議過濾

    如果抓的包有很多種協(xié)議類型,可以輸入 tcp 回車只看tcp 協(xié)議的包

  • IP 過濾

    例如  ip.src == 192.168.1.23 (過濾發(fā)起地址ip)ip.dst == 12.8.0.1(過濾目標(biāo)地址ip) ip == 12.0.0.1(過濾源或目的地址)

  • 端口過濾

    tcp.port == 4980 , 還可以  tcp.port == 4542 or tcp.port == 4528 加入表達(dá)式 and、or、in 等等

    例如:tcp.port in {80 443 8080}

  • HTTP 模式過濾

    http.request.method == "GET"  或者 http.request.method == "POST"

  • 報文內(nèi)容過濾

    tcp.segment_data contains "202005190001"  過濾tcp 報文內(nèi)容包括 202005190001 的報文

更多wireshark 過濾器可以參考:wireshark-filter

wireshark 的詳細(xì)使用教程不是今天的重點,就介紹這二部分,后面分析數(shù)據(jù)包時會穿插著講,安琪拉覺得大家有興趣可以自己抽空玩一玩這個軟件。

2.3 三次握手初探

安琪拉寫的三次握手初探 這部分如果看不懂沒關(guān)系,這里是為了介紹Wireshark寫的三次握手,后面會詳細(xì)解釋,詳細(xì)到直接從網(wǎng)絡(luò)協(xié)議分層開始講起,如果你這看不懂可以Diss 安琪拉。

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
三次握手
如果第一次看 Wireshark 網(wǎng)絡(luò)包,會一臉懵逼,看多了就會越看越喜歡。 重點看框出來的,前三行就是三次握手的過程:


  1. 上圖第一行,客戶端向服務(wù)端發(fā)送SYN 數(shù)據(jù)包,數(shù)據(jù)長度len 為0,Seq(隨機生成包序列號)為2421858999;

  2. 上圖第二行,服務(wù)端向客戶端回應(yīng)ACK 數(shù)據(jù)包,并且發(fā)送SYN 數(shù)據(jù)包,合并一起就是SYN + ACK 數(shù)據(jù)包,數(shù)據(jù)長度len 為0,Seq(隨機生成包序列號)為1988635269,ack為2421859000 = 第一次握手Seq(2421858999)+1;

  3. 上圖第三行,客戶端回應(yīng)客戶端的SYN 數(shù)據(jù)包,發(fā)送ACK 確認(rèn)數(shù)據(jù)包,Seq 為 二次握手的ack(2421859000),ack為 1988635270= 二次握手的seq(1988635269)+1;

公眾號【安琪拉的博客】后面會更新一個網(wǎng)絡(luò)系列:列舉常見的網(wǎng)絡(luò)問題,解決的思路,wireshark分析包方法等。

3. 回顧網(wǎng)絡(luò)協(xié)議分層、三次握手、四次揮手等網(wǎng)絡(luò)基礎(chǔ)知識

3.1 網(wǎng)絡(luò)協(xié)議分層

在解決文章開頭的異常,分析數(shù)據(jù)包之前,我們需要一些預(yù)備知識,需要一丟丟基礎(chǔ)的網(wǎng)絡(luò)知識。

首先在直接看Wireshark 的包信息之前,需要來回顧一下計算機網(wǎng)絡(luò)的知識,大家知道目前主流使用的TCP/IP 五層協(xié)議,而不是國際標(biāo)準(zhǔn)化組織(ISO)出的OSI(Open System Interconnection)七層協(xié)議。TCP/IP協(xié)議棧如下圖所示:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
TCP/IP五層協(xié)議

我們可以看到Wireshark 包詳情就是TCP/IP 五層的信息,對比上面的圖從下往上看(取每個英文單詞首字母就是協(xié)議簡稱,例如 HTTP:Hypertext Transfer Protocol ),如下:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
image-20200515005055601

后面我們看 Wireshark 數(shù)據(jù)報文時,主要看TCP 所在的傳輸層報文。

3.2 三次握手

首先我們先看下TCP 報文的報文格式:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
TCP報文格式

下面把TCP 報文的各個部分做了詳細(xì)說明,分析網(wǎng)絡(luò)問題不用全看,把加重的部分關(guān)注一下就可以了。好學(xué)的玩家可以把所有的都看了,不用記,有個概念就可以了。

  1. 源端口號和目的端口號:各占2個字節(jié)(16位),分別寫入源端口和目的端口;

  2. 序號:4字節(jié)(32位),TCP連接中字節(jié)流每個字節(jié)都按順序編號,這個序號用于標(biāo)識這個報文段。

    例如:一段報文序號seq 是201,而報文數(shù)據(jù)長度為100,下一個報文段的數(shù)據(jù)序號應(yīng)該為301(201+100)。

  3. 確認(rèn)號 :4字節(jié)(32位),期望收到對方下一個報文的序號。這個確認(rèn)號是和序號seq 有點關(guān)系的,不要和ACK(狀態(tài)標(biāo)志位)混淆了。

  4. 首部長度:4位,表示報文數(shù)據(jù)距離報文起始位置的長度。保留:保留今后可以會用到。

  5. 數(shù)據(jù)報狀態(tài)標(biāo)志位(非常重要),分為以下6種,二進(jìn)制1 位表示一種(1代表開啟 0 關(guān)閉)

    URG:URG=1 代表報文有緊急數(shù)據(jù)

    ACK:ACK = 1,確認(rèn)位,TCP中連接建立后,所有報文的ACK 位置都為1;

    PSH: 發(fā)送端和接收端都有緩沖區(qū)(發(fā)送端:寫緩沖區(qū) 接收端:讀緩沖區(qū)) 對于發(fā)送端:帶PSH=1,報文會立即從緩沖區(qū)報文推送給服務(wù)端  對于服務(wù)端:服務(wù)端立即將讀緩沖區(qū)內(nèi)容推給進(jìn)程。

    RST:RST=1,代表連接出現(xiàn)嚴(yán)重錯誤,TCP連接的一方將連接重置了,必須釋放連接,重新建立連接;

    SYN:同步SYN,在連接建立時用來同步序號。三次握手時會用到,當(dāng)SYN=1,ACK =0,表明是發(fā)起方請求建立連接,服務(wù)方同意建立連接,響應(yīng)報文SYN=1,ACK =1,前者表明同步連接,后者是確認(rèn)報文。

    FIN:用來釋放連接。當(dāng)FIN =1,表明此報文的發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完畢,并且要求釋放。

  6. 窗口:占2字節(jié),通常用于告知對方自己的能夠接受的數(shù)據(jù)量大小。窗口本質(zhì)就是一個緩沖區(qū)buffer,該字段的值用于告知對方自己剩余的可用緩沖區(qū)大小。

  7. 校驗和:奇偶校驗,此校驗和是對整個的 TCP 報文段,包括 TCP 頭部和 TCP 數(shù)據(jù),以 16 位字進(jìn)行計算所得。由發(fā)送端計算和存儲,并由接收端進(jìn)行驗證。

  8. 緊急指針:只有當(dāng) URG 標(biāo)志置 1 時緊急指針才有效。緊急指針是一個正的偏移量,和順序號字段中的值相加表示緊急數(shù)據(jù)最后一個字節(jié)的序號。

  9. 選項:可選的。最常見的可選字段是最長報文大小,又稱MSS(Maxinum Segment Size), 每個連接方通常在通信的第一個報文段(連接建立的SYN標(biāo)志位為1的數(shù)據(jù)報文)設(shè)置這個選項,表示本端能接受的最大報文段的長度。因為長度不一定是32的整數(shù)倍,因此要加額外的0作為填充。

  10. 數(shù)據(jù)部分:可選的。連接建立和終止時,報文段只有TCP首部。

我們先回顧一下以前計算機網(wǎng)絡(luò)課堂上學(xué)過的TCP傳輸?shù)娜挝帐至鞒蹋?/p>

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
TCP連接三次握手
遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
三次握手

三次握手的具體過程如下:

  1. 服務(wù)端進(jìn)程啟動,準(zhǔn)備接收客戶端進(jìn)程的連接請求,此時接收方進(jìn)入LISTEN(監(jiān)聽)模式;

  2. 三次握手第一步:客戶端向服務(wù)端發(fā)出連接請求報文,這時報文首部SYN 標(biāo)志位為1,同時設(shè)置一個初始序列號seq = x(隨機數(shù)); 做完這步動作,發(fā)送方進(jìn)入SYN_SENT (同步已發(fā)送狀態(tài)) 。

    名稱解釋:SYN:同步標(biāo)志位  seq:包序列編號(每個包都有一個序列號)

    第一次握手客戶端發(fā)送的報文稱為同步請求報文,希望與服務(wù)端建立同步連接,SYN報文不攜帶數(shù)據(jù)。

  3. 三次握手第二步:服務(wù)端收到來自客戶端的連接請求報文后,需要確認(rèn)收貨,響應(yīng)報文中ACK(確認(rèn)標(biāo)志位)設(shè)置為1,將確認(rèn)號ack 設(shè)置為第一步的請求序列號seq 加1(ack =x+1),另外自己也回客戶端一個SYN包(可以建立同步連接),即SYN + ACK包,包序列號seq = y,服務(wù)端進(jìn)入SYN_RCVD(同步收到)狀態(tài)。

    名詞解釋:ACK:確認(rèn)狀態(tài)位(這里ACK=1),這個一定和ack(32位確認(rèn)序號,這里ack=x+1)區(qū)分開,可以看下面的TCP 報文結(jié)構(gòu)體圖,ACK是包的狀態(tài)標(biāo)志,ack是確認(rèn)序號。

  4. 三次握手第三步:客戶端收到來自服務(wù)端的 SYN + ACK 包,會發(fā)送一個ACK 確認(rèn)包,ACK =1,seq = x+1( 第二步的ack),ack = y+1(第二步的seq+1)。

玩家們?nèi)绻X得看安琪拉寫的有收獲,歡迎關(guān)注公眾號【安琪拉的博客】,來找安琪拉草叢互動!

3.3 四次揮手

四次揮手的狀態(tài)圖如下所示:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
四次揮手

四次揮手wireshark 包信息如下,可以對照著上圖看,

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招

Wireshark四次揮手

四次揮手的具體過程如下:

  1. 客戶端發(fā)送FIN 釋放連接報文,表示結(jié)束連接,報文seq = u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個字節(jié)的序號加1),此時,客戶端進(jìn)入FIN_WAIT1(終止等待1)狀態(tài)。

  2. 服務(wù)器收到連接釋放報文,發(fā)出確認(rèn)報文,ACK=1,ack=u+1,并且?guī)献约旱男蛄刑杝eq=v,此時,服務(wù)端就進(jìn)入了CLOSE_WAIT(關(guān)閉等待)狀態(tài)。TCP接收方通知上層的應(yīng)用進(jìn)程,客戶端向服務(wù)器方向的發(fā)送通道關(guān)閉了,這時候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了(已經(jīng)發(fā)了FIN結(jié)束信號),但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個狀態(tài)要持續(xù)一段時間,也就是整個CLOSE_WAIT狀態(tài)持續(xù)的時間。

  3. 客戶端收到服務(wù)器的確認(rèn)請求后,此時,客戶端就進(jìn)入FIN_WAIT2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報文(在服務(wù)端Close_Wiat期間還可以接受服務(wù)器發(fā)送的最后的數(shù)據(jù))。

  4. 服務(wù)端發(fā)送完最后的數(shù)據(jù),向客戶端發(fā)送FIN 連接釋放報文,ACK =1,由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時的序列號為seq=w,ack 和回復(fù)ACK報文一致,ack = u+1, 此時,服務(wù)器就進(jìn)入了LAST_ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。

  5. 客戶端收到服務(wù)器的連接釋放報文后,必須發(fā)出確認(rèn),ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進(jìn)入了TIME_WAIT(時間等待)狀態(tài)。注意此時TCP連接還沒有釋放,必須經(jīng)過2 個MSL(最長報文段壽命)的時間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入CLOSED狀態(tài)。

  6. 服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)。同樣,撤銷TCB后,就結(jié)束了這次的TCP連接。

4.異常定位:Wireshark 分析定位Broken Pipe 異常原因

4.1 網(wǎng)絡(luò)包分析

上面我們已經(jīng)看了正常的四次揮手的流程和截圖,下面我們來看下線上遇到的 Broken Pipe異常,再次看一眼異常棧,如下圖:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
網(wǎng)絡(luò)異常棧

我們可以看Wireshark 的包信息如下圖,

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招

可以看到,我們來看一下流程,第一步是區(qū)分服務(wù)端和客戶端:

  • 服務(wù)端:IP后綴為132 ,端口20004是,

  • 客戶端:IP后綴為156,端口為4528。

前三行是三次握手,沒有問題,但是最后的四次揮手這里有問題(可以對照著四次揮手的圖看):

  • No.189 行:服務(wù)端發(fā)起FIN 報文希望關(guān)閉連接,服務(wù)端進(jìn)入FIN_WAIT1 狀態(tài);

  • No.190 行:客戶端響應(yīng)服務(wù)端的FIN 報文發(fā)送ACK 報文,進(jìn)入CLOSE_WAIT 狀態(tài);

  • No.191 ~ No.197:服務(wù)端接收到客戶端的ACK 進(jìn)入FIN_WAIT2 狀態(tài),此時服務(wù)端是不接收數(shù)據(jù)傳輸?shù)?,但是我們可以看到Wireshark 191 ~ 196 行客戶端還在發(fā)送數(shù)據(jù)報文,正常應(yīng)該是客戶端發(fā)送FIN 報文關(guān)閉連接,讓服務(wù)端進(jìn)入TIME_WAIT 狀態(tài),但是客戶端沒有發(fā)送FIN報文,而是向已經(jīng)準(zhǔn)備關(guān)閉的連接通道中發(fā)送了數(shù)據(jù)報文,因為服務(wù)端不認(rèn)你客戶端的數(shù)據(jù),所以發(fā)送了RST 信號報文來重置連接。

下面是正常的四次揮手和異常的四次揮手對照圖:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
對比
4.2 問題定位
  1. 供應(yīng)商接口查看狀態(tài)

    讓HTTP 請求的被調(diào)用方(供應(yīng)商)查看當(dāng)前網(wǎng)絡(luò)狀態(tài),Linux 命令如下所示:

    1netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'

    結(jié)果如下:

    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
    image-20200522214047234

    可以對照著四次揮手流程圖看狀態(tài):

    ESTABLISHED:  處于連接建立狀態(tài)的連接數(shù)

    FIN_WAIT1: 處于連接關(guān)閉FIN_WAIT1 狀態(tài)的連接數(shù)

    FIN_WAIT2: 處于連接關(guān)閉FIN_WAIT2 狀態(tài)的連接數(shù)

    TIME_WAIT:處于 TIME_WAIT 狀態(tài)的連接數(shù)

    可以看到TIME_WAIT 狀態(tài)很多,這個是正常的,只要記住,正常四次揮手流程中,主動關(guān)閉的一方會經(jīng)過TIME_WAIT 狀態(tài),被動關(guān)閉一方會經(jīng)過 CLOSE_WAIT 狀態(tài),這二個狀態(tài)(TIME_WAIT & CLOSE_WAIT)需要做個區(qū)分,如果 CLOSE_WAIT 狀態(tài)過多可能會有問題,這個我會在擴(kuò)展閱讀詳細(xì)說,繼續(xù)分析異常。

  2. 確認(rèn)連接方式

    通過上面Wireshark 異常包可以知道是服務(wù)端進(jìn)入FIN_WAIT2 狀態(tài)后,客戶端繼續(xù)發(fā)送數(shù)據(jù)包,導(dǎo)致服務(wù)端RST 連接。這里有二個問題:

  3. 為什么服務(wù)端主動發(fā)起FIN 關(guān)閉連接呢?

  4. 為什么客戶端在接收到服務(wù)端的FIN 并回復(fù)ACK 報文之后,為什么沒有發(fā)送FIN 關(guān)閉連接報文呢?

    不賣關(guān)子了,講了這么多吊足了玩家們的胃口,真實原因都不好意思講了,其實是因為供應(yīng)商不支持長連接,但是我們?yōu)榱速Y源復(fù)用,降低HTTP 連接創(chuàng)造銷毀的開銷,使用了連接池,連接池的連接是復(fù)用的,是長連接,所以才會出現(xiàn)服務(wù)端第二次發(fā)送數(shù)據(jù)時不進(jìn)行三次握手,而是直接傳輸數(shù)據(jù)報文的情況。

5. 代碼修復(fù):調(diào)整客戶端代碼

5.1 HttpClient 長連接改為短連接
  1. 服務(wù)端那邊配置的Nginx 是短連接方式,客戶端代碼使用長連接,客戶端HTTP 請求的代碼(刪除了部分代碼),請求結(jié)束之后,連接不進(jìn)行關(guān)閉,連接池復(fù)用連接。如下:

    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招

    長連接主要代碼

    如果要支持長連接,服務(wù)端Nginx 配置需要做些修改,如下:

     1http{
    2 keepalive_timeout 60s;  #連接保持的超時時間
    3
    4 upstream servers {
    5       keepalive 10;
    6   }
    7
    8 server {
    9    listener 20004 so_keepalive=on #這個是支持長連接的配置
    10   }
    11}


  2. 代碼問題修復(fù)

    客戶端連接方式從長連接改成短連接,設(shè)置連接不復(fù)用就可以了,如下:

    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
    image-20200525203700284
  3. 番外

    從現(xiàn)象上看因為長連接的連接復(fù)用,導(dǎo)致了 Broken Pipe 異常,但是從實際業(yè)務(wù)角度,業(yè)務(wù)測試成功率沒有明顯變化。因為連接池設(shè)置了異常重試策略,如果發(fā)生Broken Pipe 異常,會再從連接池獲取連接重試,重試的成功率非常高,因為大多數(shù)時候是新建的連接來重試。這點從wireshark 網(wǎng)絡(luò)包中可以看出。

    因為每一筆業(yè)務(wù)都有流水號,因為可以使用Wireshark 過濾器過濾網(wǎng)絡(luò)包的數(shù)據(jù)內(nèi)容,如下所示:

    過濾器輸入業(yè)務(wù)日志中的請求的流水號,過濾器過濾包的命令如下:

    1tcp.segment_data contains "TCP包中業(yè)務(wù)流水號"
    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
    image-20200525205104904

    如上圖所示,可以看到有二筆TCP 數(shù)據(jù)包中數(shù)據(jù)包含有這個業(yè)務(wù)流水號,這個就是因為第一次TCP 失敗后重試造成的,可以看到一筆是從4528端口發(fā)出,一筆是4542端口發(fā)出的,我們可以把這二筆單獨拉出來看一下,使用過濾器的命令如下,4542端口失敗后新建一個連接(端口為4528)發(fā)送數(shù)據(jù):

    1tcp.port == 4542 or tcp.port == 4528

    過濾后的數(shù)據(jù)包如下圖所示:

    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
    wireshark過濾數(shù)據(jù)包

6. 擴(kuò)展閱讀:其他網(wǎng)絡(luò)知識及面試題

6.1 為什么需要三次握手來建立TCP 連接?

答:第一次握手客戶端發(fā)送報文給服務(wù)端,收到服務(wù)端的應(yīng)答表明客戶端發(fā)送數(shù)據(jù)的能力ok;第二次握手服務(wù)端發(fā)送數(shù)據(jù)給客戶端表示服務(wù)端接收數(shù)據(jù)能力ok(我正常收到你的數(shù)據(jù)了,告訴你一聲);第三次客戶端發(fā)報文給服務(wù)端表明服務(wù)端的發(fā)送數(shù)據(jù)的能力也ok,客戶端接收數(shù)據(jù)能力也ok(我能正常收到你的數(shù)據(jù),代表你發(fā)的數(shù)據(jù)沒問題,我的接收能力也沒問題,所以告知你一聲)。所以要驗證客戶端和服務(wù)端發(fā)送&接收數(shù)據(jù)的能力都o(jì)k至少需要三次握手才能達(dá)到。舉個實際的例子,比如你投遞簡歷,相當(dāng)于第一次握手,HR回復(fù)你簡歷已收到,相當(dāng)于第二次握手,你回復(fù)HR已收到批準(zhǔn)通知了,這相當(dāng)于三次握手,HR可以給你安排面試了。因為每一次握手都有消息丟失的風(fēng)險,所以需要往返至少三次才能保證連接的建立。

6.2 TIME_WAIT 和 CLOSE_WAIT 有什么區(qū)別?

答:這二個狀態(tài)是四次揮手中的狀態(tài),TIME_WAIT 是主動關(guān)閉的一方發(fā)出 FIN 包會經(jīng)過的狀態(tài),CLOSE_WAIT 是被動關(guān)閉連接的一端會經(jīng)過的狀態(tài)。TIME_WAIT 經(jīng)過2個MSL(最大報文段生存時間)才能到CLOSE狀態(tài),CLOSE_WAIT 如果不發(fā)送FIN 報文會一直處在CLOSE_WAIT 狀態(tài)。所以一般在看機器連接狀態(tài),幾千個TIME_WAIT 一般是正常的(過2MSL自動關(guān)閉),處于CLOSE_WAIT 狀態(tài)的連接很多,證明有問題。

6.3 處于CLOSE_WAIT 狀態(tài)的連接很多,怎么辦?

答:處于CLOSE_WAIT 狀態(tài)的連接很多,證明有問題,有幾種可能:

  • 代碼問題:短連接模式,忘記 close 連接,就不會發(fā)出 FIN 包,導(dǎo)致連接處于 CLOSE_WAIT狀態(tài);或者程序在close 連接之前陷入死循環(huán)或者執(zhí)行時間過長;

  • backlog太大:backlog太大這里指的accept 的連接隊列設(shè)置的太大,這個參數(shù)是在服務(wù)端創(chuàng)建ServerSocket作為參數(shù)傳入的,默認(rèn)為50,支持自定義,設(shè)置的太少容易出現(xiàn)連接reset或者拒絕,太大如果服務(wù)端處理連接不及時會放到accept隊列等待太長時間。accept隊列以及socket 連接建立流程如下圖:

    遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
    image-20200527234141069

上圖所示,這里有兩個隊列:syns queue(半連接隊列);accept queue(全連接隊列)。我們很多時候排查網(wǎng)絡(luò)問題時也需要考慮到accept queue,很多場景需要對accept queue 大小做些調(diào)整。

6.4 如何判斷是否需要調(diào)整 accept queue(全連接隊列)大???

答:例如我們機器并發(fā)量很高,accept queue(全連接隊列) 可能會出現(xiàn)不夠用的情況,會出現(xiàn)類似connection reset 和 connection timeout 異常,這個取決于機器上 tcp_abort_on_overflow 的設(shè)置,不同值服務(wù)端不同處理機制:

tcp_abort_on_overflow為0:連接建立過程中三次握手第三步時,發(fā)生全連接隊列滿了,server扔掉client 發(fā)過來的ack,那么client 會重新發(fā)送ack,直到超時,所以客戶端會出現(xiàn)連接超時(connection timeout );

tcp_abort_on_overflow為1:遇到全連接隊列滿了,server會發(fā)一個reset包給client,表示廢掉這個這個連接,這個握手過程無效,客戶端會看到很多connection reset by peer的錯誤;

查看服務(wù)器處理accept queue 隊列滿時的處理機制:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
image-20200527235838846

那怎么判斷服務(wù)端有沒有發(fā)生accept queue(全連接隊列)滿呢?在linux 可以采用如下指令:

1netstat -s | grep "listen"

每隔幾秒打印一次,如下圖所示:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
image-20200527235626558

如果overflowed 代表隊列溢出次數(shù)一直在增加,是accept 隊列大小不夠或者隊列中連接等待處理時間太長導(dǎo)致的。

另外通過ss 命令可以看具體端口的隊列占用情況,如下所示:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
image-20200528000349200

Send-Q 這列代表第四列端口上全連接隊列的最大值,Recv-Q 代表當(dāng)前隊列有多少個連接,如果長期是滿的,代表可以適當(dāng)增加全連接隊列大小了。

6.5 如何修改accept queue(全連接隊列)大小呢?

答:accept queue(全連接隊列)取決于 min(backlog, somaxconn) ,backlog是在socket創(chuàng)建的時候傳入的,somaxconn是一個os級別的系統(tǒng)參數(shù),somaxconn 定義了系統(tǒng)中每一個端口最大的監(jiān)聽隊列的長度, 這是個全局的參數(shù), 默認(rèn)值為128。

- 可以通過 echo 1024 >/proc/sys/net/core/somaxconn 來臨時修改somaxconn這個參數(shù);

- 可以/etc/sysctl.conf 中添加如下  net.core.somaxconn = 1024 然后在終端中執(zhí)行sysctl -p 讓配置永久生效;

對于Java后端程序,在創(chuàng)建Socket 時是可以傳入?yún)?shù)設(shè)置backlog 大小的,很多人可能沒關(guān)注過這個這個參數(shù),看到很多程序直接使用的默認(rèn)構(gòu)造函數(shù)創(chuàng)建Socket 的。

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招
服務(wù)端Socket構(gòu)造函數(shù)
參考自JDK 1.8 文檔


如上圖所示,在創(chuàng)建Socket 時,可以通過傳入backlog 設(shè)置全連接隊列大小。如果不傳這個參數(shù),默認(rèn)值是50,Tomcat 默認(rèn)短連接,這個值默認(rèn)是100, Nginx默認(rèn)是511。

6.6 為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL(最大報文段生存時間)才能返回到CLOSE狀態(tài)?

答:雖然按道理,揮手過程中的四個報文都發(fā)送完畢,主動關(guān)閉連接的一方應(yīng)當(dāng)是可以直接進(jìn)入CLOSE狀態(tài)了,但是必須考慮到網(wǎng)絡(luò)環(huán)境是十分復(fù)雜且不可靠的,有可以四次揮手最后一個ACK丟失。所以TIME_WAIT狀態(tài)就是用來重發(fā)可能丟失的ACK報文,假設(shè)在Client發(fā)送出最后的ACK回復(fù),但該ACK 如果丟失,Server沒有收到ACK,Server會重復(fù)發(fā)送FIN片段。所以Client不能立即關(guān)閉,它必須確認(rèn)Server接收到了第四次揮手的ACK。Client會在發(fā)送出ACK之后進(jìn)入到TIME_WAIT狀態(tài)。Client會設(shè)置一個計時器,等待2MSL的時間。如果在該時間內(nèi)再次收到FIN,那么Client會重發(fā)ACK并再次等待2MSL。這個2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網(wǎng)絡(luò)中最大的存活時間,2MSL就是一個發(fā)送加上一個回復(fù)所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那么Client推斷ACK已經(jīng)被成功接收,則結(jié)束TCP連接。

6.7 如果三次握手連接已經(jīng)建立,但是客戶端突然出現(xiàn)故障了怎么辦?

答:首先,從協(xié)議設(shè)計角度來講,客戶端如果出現(xiàn)故障,服務(wù)端肯定不能一直死等客戶端,所以考慮這種情況的存在,TCP 協(xié)議中服務(wù)端有個計時器,每次收到客戶端的響應(yīng)報文都會重置這個計時器,服務(wù)端有個超時時間,通常是2個小時,2個小時沒收到客戶端的數(shù)據(jù),服務(wù)端會每隔75秒發(fā)送探測報文段,連續(xù)10次探測報文沒響應(yīng),認(rèn)為客戶端出現(xiàn)問題,服務(wù)器會關(guān)閉這個連接。一般程序設(shè)計者不會依賴這個機制,2個小時實在太長,框架里面都會自己做連接的檢查,無效連接的關(guān)閉,例如連接池的連接驅(qū)逐策略,Apollo等框架的連接?;畹?。

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招

長按訂閱更多精彩▼

遇到網(wǎng)絡(luò)問題你是怎么解決的?安琪拉有二招

如有收獲,點個在看,誠摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉