多队列 部分队列没有包_记一次TCP全队列溢出问题排查过程
1. 前言
本文排查的問(wèn)題是經(jīng)典的TCP隊(duì)列溢出問(wèn)題,因TCP隊(duì)列問(wèn)題在操作系統(tǒng)層面沒(méi)有明顯的指標(biāo)異常,容易被忽略,故把排查過(guò)程分享給大家。
2. 問(wèn)題描述
A服務(wù)調(diào)用B服務(wù)接口超時(shí),B服務(wù)主機(jī)IOWAIT高,具體超時(shí)情況分為兩種:
- A服務(wù)的請(qǐng)求在B服務(wù)日志中可查到,但B服務(wù)的響應(yīng)時(shí)間超過(guò)了A服務(wù)的等待超時(shí)時(shí)間3S。
- A服務(wù)的請(qǐng)求在B服務(wù)日志中無(wú)法查到。
3. 問(wèn)題分析
此種超時(shí)請(qǐng)求集中在很短的一段時(shí)間(通常在2分鐘之內(nèi)),過(guò)后便恢復(fù)正常,所以很難抓到問(wèn)題現(xiàn)場(chǎng)分析原因,只能搭建測(cè)試環(huán)境,A服務(wù)持續(xù)請(qǐng)求B服務(wù),在B服務(wù)主機(jī)上通過(guò)DD命令寫(xiě)入大量數(shù)據(jù)造成主機(jī)IOWAIT高,同時(shí)通過(guò)TCPDUMP在兩端抓包分析。
部分服務(wù)超時(shí)日志:
- 服務(wù)A:Get http://xxx&id=593930: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
- 服務(wù)B: "GET xxx&id=593930 HTTP/1.1" 200 64 "-" "Go-http-client/1.1" "-" "-" 165000(單位微秒)
服務(wù)A發(fā)起請(qǐng)求3S后沒(méi)有收到服務(wù)B響應(yīng),斷開(kāi)連接,服務(wù)B日志顯示處理時(shí)長(zhǎng)為0.165S,遠(yuǎn)低于3S,服務(wù)A側(cè)看服務(wù)B的響應(yīng)時(shí)間為網(wǎng)絡(luò)傳輸時(shí)間、TCP隊(duì)列排隊(duì)時(shí)間及服務(wù)B應(yīng)用程序處理時(shí)間之和,因?yàn)槭莾?nèi)網(wǎng)測(cè)試,網(wǎng)絡(luò)傳輸時(shí)間可以忽略,主要排查方向應(yīng)為T(mén)CP隊(duì)列排隊(duì)時(shí)間。
4. 抓包數(shù)據(jù)分析
情景1:服務(wù)A及服務(wù)B均有連接日志打印。
服務(wù)A端數(shù)據(jù)包分析:
09:51:43.966553000 服務(wù)A發(fā)起 GET請(qǐng)求的數(shù)據(jù)包如下:
09:51:46.966653000 服務(wù)A發(fā)起 GET請(qǐng)求3s(即服務(wù)A設(shè)置的等待超時(shí)時(shí)長(zhǎng))后,因未收到服務(wù)B響應(yīng),服務(wù)A向服務(wù)B發(fā)起FIN主動(dòng)斷開(kāi)連接。
09:51:59.958195000 服務(wù)A發(fā)起http請(qǐng)求16s后收到服務(wù)B的http響應(yīng)報(bào)文,因服務(wù)A已主動(dòng)關(guān)閉該連接,故直接回復(fù)RST。
服務(wù)B端數(shù)據(jù)包分析:
09:51:44.062095000 服務(wù)B收到服務(wù)A發(fā)送的http請(qǐng)求包。
9:51:59.936169000 服務(wù)B響應(yīng)服務(wù)A,服務(wù)B從接收到http請(qǐng)求報(bào)文至響應(yīng)http請(qǐng)求總用時(shí)約為15s多,但服務(wù)B打印的日志響應(yīng)時(shí)長(zhǎng)約為0.165s。
情景2:服務(wù)A有連接日志,服務(wù)B無(wú)連接日志。
服務(wù)A端數(shù)據(jù)包分析:
09:51:43.973791000 服務(wù)A向服務(wù)B發(fā)送一個(gè)http請(qǐng)求數(shù)據(jù)包,隨后收到服務(wù)B重傳的第二次握手的syn+ack包,超過(guò)3s未收到服務(wù)B的http響應(yīng)后斷開(kāi)連接。
服務(wù)B端數(shù)據(jù)包分析:
服務(wù)B重傳了第二次握手的syn+ack包,收到服務(wù)A的http請(qǐng)求,服務(wù)B忽略,未響應(yīng),服務(wù)A等待超時(shí)后斷開(kāi)了連接。
5. 根因分析
TCP在三次握手過(guò)程中內(nèi)核會(huì)維護(hù)兩個(gè)隊(duì)列:
- 半連接隊(duì)列,即SYN隊(duì)列
- 全連接隊(duì)列,即ACCEPT隊(duì)列
TCP三次握手過(guò)程中,第一次握手server收到client的syn后,內(nèi)核會(huì)把該連接存儲(chǔ)到半連接隊(duì)列中,同時(shí)回復(fù)syn+ack給client(第二次握手),第三次握手時(shí)server收到client的ack,如果此時(shí)全連接隊(duì)列未滿(mǎn),內(nèi)核會(huì)把連接從半連接隊(duì)列移除,并將其添加到 accept 隊(duì)列,等待應(yīng)用進(jìn)程調(diào)用 accept 函數(shù)取出連接,如果全連接隊(duì)列已滿(mǎn),內(nèi)核的行為取決于內(nèi)核參數(shù)tcp_abort_on_overflow:
- tcp_abort_on_overflow=0,server會(huì)丟棄client的ack。
- tcp_abort_on_overflow=1,server 會(huì)發(fā)送 reset 包給 client。
默認(rèn)值是0。
情景1的抓包數(shù)據(jù)顯示連接已經(jīng)進(jìn)入全連接隊(duì)列,但是服務(wù)B日志顯示的連接時(shí)間晚了15S多,說(shuō)明連接在隊(duì)列里等待了15S后才被應(yīng)用處理。
情景2的抓包數(shù)據(jù)顯示全連接隊(duì)列已溢出,內(nèi)核根據(jù)tcp_abort_on_overflow的值為0丟棄了服務(wù)A的ack,超過(guò)了服務(wù)A的超時(shí)等待時(shí)間。
結(jié)論:服務(wù)B主機(jī)在IO達(dá)到瓶頸的情況下,系統(tǒng)CPU時(shí)間主要消耗在等待IO響應(yīng)及處理軟中斷上,服務(wù)B應(yīng)用程序獲取的CPU時(shí)間有限,無(wú)法及時(shí)調(diào)用 accept 函數(shù)把連接取出并處理,導(dǎo)致TCP全隊(duì)列溢出或隊(duì)列等待時(shí)間過(guò)長(zhǎng),超過(guò)了服務(wù)A的超時(shí)時(shí)間。
6. 如何觀察和調(diào)整tcp全隊(duì)列
圖10: TCP全隊(duì)列觀察方法當(dāng)連接處于listen狀態(tài)時(shí):
- Recv-Q:目前全連接隊(duì)列的大小
- Send-Q:目前全連接最大隊(duì)列長(zhǎng)度
當(dāng)Recv-Q > Send-Q時(shí)表示全隊(duì)列溢出,可通過(guò)執(zhí)行netstat -s | grep "overflowed"命令觀察溢出情況,查看累計(jì)溢出次數(shù),如果需觀察一段時(shí)間內(nèi)的全隊(duì)列溢出情況,建議使用監(jiān)控系統(tǒng)采集數(shù)據(jù),比如prometheus。
圖11: TCP隊(duì)列溢出監(jiān)控
TCP 全連接隊(duì)列最大值取決于min(somaxconn, backlog),其中:
- somaxconn可通過(guò)內(nèi)核參數(shù)/proc/sys/net/core/somaxconn設(shè)置,默認(rèn)值是128。
- backlog是 listen(int sockfd, int backlog) 函數(shù)中的 backlog 大小,Nginx 默認(rèn)值是 511,可以通過(guò)修改配置文件設(shè)置其長(zhǎng)度。
7. 結(jié)語(yǔ)
本次問(wèn)題,因?yàn)榉?wù)對(duì)成功率要求很高,所以先通過(guò)調(diào)大服務(wù)B主機(jī)/proc/sys/net/core/somaxconn參數(shù)值及服務(wù)A的超時(shí)時(shí)間來(lái)緩解超時(shí)問(wèn)題,暫時(shí)保證了接口成功率。但要從根本上解決問(wèn)題,仍需解決誘因io瓶頸,因?yàn)榉?wù)B主機(jī)掛載的共享sas存儲(chǔ)集群上有其他客戶(hù)的主機(jī)偶爾io很大,影響了整個(gè)集群的性能。為解決此問(wèn)題,更換為獨(dú)享的ssd盤(pán),并通過(guò)blktrace+fio分析,將io調(diào)度算法修改為noop,io性能明顯提升,TCP隊(duì)列溢出問(wèn)題也隨之解決。
作者:陳立華
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載
總結(jié)
以上是生活随笔為你收集整理的多队列 部分队列没有包_记一次TCP全队列溢出问题排查过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: webuploader常用知识及方法、网
- 下一篇: 16 Babylonjs基础入门 阴影