首頁>技術>

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請求的資料包如下:

圖1:服務A發起GET請求

09:51:46.966653000 服務A發起 GET請求3s(即服務A設定的等待超時時長)後,因未收到服務B響應,服務A向服務B發起FIN主動斷開連線。

圖2:服務A等待超時主動斷開連線

09:51:59.958195000 服務A發起http請求16s後收到服務B的http響應報文,因服務A已主動關閉該連線,故直接回復RST。

圖3: 服務B16s後響應

服務B端資料包分析:09:51:44.062095000 服務B收到服務A傳送的http請求包。

圖4:服務B收到服務A的請求

09:51:59.936169000 服務B響應服務A,服務B從接收到http請求報文至響應http請求總用時約為15s多,但服務B列印的日誌響應時長約為0.165s。

圖5:服務B15S後響應

圖6:服務B日誌顯示響應時間0.165s

情景2:服務A有連線日誌,服務B無連線日誌。服務A端資料包分析:09:51:43.973791000 服務A向服務B傳送一個http請求資料包,隨後收到服務B重傳的第二次握手的syn+ack包,超過3s未收到服務B的http響應後斷開連線。

圖7:服務B重傳syn+ack

服務B端資料包分析:服務B重傳了第二次握手的syn+ack包,收到服務A的http請求,服務B忽略,未響應,服務A等待超時後斷開了連線。

圖8: 服務B忽略服務A請求

5. 根因分析

TCP在三次握手過程中核心會維護兩個佇列:

半連線佇列,即SYN佇列全連線佇列,即ACCEPT佇列

圖9:TCP佇列

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佇列溢位問題也隨之解決。

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 如何對音樂 app 收集到使用者資訊進行資料建模?