Broken pipe異常分析及解決
Broken pipe異常分析報告
1.錯誤描述
ClientAbortException: java.io.IOException: Broken pipe
可能出現原因:
TCP服務端write資料時,收到SIGPIPE訊號(連線已經終止)
場景:
- TCP握手尚未結束時,連線已經close;
- 服務端收到一次read,但write了多次;
- 連線通道被佔滿,新連線被拒絕時,client中斷了所有連線。
2.分析過程
2.1.初步排查
每次出現該異常時,總是伴隨/im/getUsercInfos.json介面的呼叫。
2.1.1具體現象
- 異常記錄時間=介面請求記錄時間(server連線write前記錄)-(2至10)ms;
- 總是ios裝置爆出;
- 總是h端爆出。
2.1.2分析
可能性一:
在請求該介面時,ios裝置在某種情況下會中斷該請求,導致TCP連線中server端無法向client端write資料。即在server端write之前連線已經close,write時出現異常。
由於只有h端出現,但h、b客戶端程式碼一致,不太可能單獨出現,故可能性較低。可能性二:
在第一次連線已經正常握手,並正常close後,server端在此執行write操作。即對一個對端已經關閉的socket呼叫兩次write,報出SIGPIPE訊號,導致異常。
由於只有ios裝置有該問題,故可能性較低。可能性三:
請求介面本身問題或併發呼叫問題,引起了連線close或連線過多超限導致client端close連線。
目前acceptCount配置100,觀察日誌,報錯時間區間中並沒有如此大量的併發請求,且client端收到拒絕資訊時,不確定是否會中斷所有請求(理論上不會)。故可能性較低。
2.2.細緻排查
伴隨該異常的介面,入參和出參量較大(獵頭端im對話列表1000-3000個是常態),介面處理時間長,且伴隨不同程度的連續請求。
2.2.1具體現象
- 入參傳入emNames數量600-4000個,約15000-100000字元長度;
- 出參返回約60000-500000字元長度;
- 執行時常約500-4000ms;
2.2.2分析
可能性一:
處理報文過長,client端無法解析/處理過大報文,導致本次及下次請求。
客戶端系統對於大報文的處理問題需要測試和調研,故有可能性。可能性二:
處理時間過長,導致當client端併發請求時,當上次請求尚未完成,下次請求會close上次請求,以本次為準。
調研後發現client端是阻塞請求,但需要進行實際測試,故有可能性。可能性三:
處理時間過長,在握手過程中client自行中斷了連線。
由於執行時間較長,頻率較高,客戶或裝置自己可能觸發kill程序或關閉連線等操作,故有可能性。
3.測試過程
3.1.場景設計
- 服務端:
返回引數量大、處理時間長的介面 - 客戶端:
h端客戶端;大量輸入引數;併發呼叫介面的工具 - 介面:
URL:/im/getTests.json
入參:emNames (List,每條約24字元)
出參:PageForm
邏輯:根據emNames數量,組裝並返回同等數量的PageForm(每條約250字元),根據需求sleep若干時間(3000-5000ms)。
3.2.測試報告
3.2.1.IOS
ios,4000引數,3次併發,服務端無sleep: 無問題
ios,4000引數,3次併發,服務端sleep 3000ms: 無問題
ios,2000引數,20次併發,服務端sleep 5000ms,中途client切出: 無問題
ios,2000引數,20次併發,服務端sleep 5000ms,中途client中斷: 發現問題,完全符合異常現象
3.2.2.ANDROID
- android,2000引數,10次併發,服務端sleep 5000ms,中途client中斷:發現問題,完全符合異常現象
4.結論
4.1.問題原因
client端使用者在殺死程序時,介面的TCP請求尚未完成(未完成的原因是處理時間長)。
導致server端write資料時,收到SIGPIPE訊號,丟擲Broken pipe異常。
但由於已經殺死了程序,並不會對使用者產生任何影響。
4.2.其他結論
- ios和android在處理1M以內(尚不清楚有沒有最大值)大小報文時,沒有問題
- ios和android均是阻塞請求,本次請求不會對上次請求造成影響,即使上次請求尚未完成
- ios切換至後臺,短時間內(5000ms以內,最大值根據系統不同而不同)並不會中斷已經存在的TCP連線。
- android切換至後臺,並不會中斷已經存在的TCP連線。
- ios、android殺死程序會一併關閉已經存在的TCP連線。
- android框架(公司基於google改造的框架)預設併發5執行緒一組。
5.解決方案
由於kill程序我們無法控制,故只能通過降低介面處理時間,減少使用者kill程序時未完成的TCP連線數量。
具體:
- 會話列表翻頁
- 會話列表限制展示數量
- 客戶端分組獲取會話列表資料