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