1. 程式人生 > >Broken pipe異常分析及解決

Broken pipe異常分析及解決

Broken pipe異常分析報告

1.錯誤描述

ClientAbortException: java.io.IOException: Broken pipe

可能出現原因:

TCP服務端write資料時,收到SIGPIPE訊號(連線已經終止)

場景:

  1. TCP握手尚未結束時,連線已經close;
  2. 服務端收到一次read,但write了多次;
  3. 連線通道被佔滿,新連線被拒絕時,client中斷了所有連線。

2.分析過程

2.1.初步排查

每次出現該異常時,總是伴隨/im/getUsercInfos.json介面的呼叫。

2.1.1具體現象
  • 異常記錄時間=介面請求記錄時間(server連線write前記錄)-(2至10)ms;
  • 總是ios裝置爆出;
  • 總是h端爆出。
2.1.2分析
  1. 可能性一:
    在請求該介面時,ios裝置在某種情況下會中斷該請求,導致TCP連線中server端無法向client端write資料。即在server端write之前連線已經close,write時出現異常。
    由於只有h端出現,但h、b客戶端程式碼一致,不太可能單獨出現,故可能性較低。

  2. 可能性二:
    在第一次連線已經正常握手,並正常close後,server端在此執行write操作。即對一個對端已經關閉的socket呼叫兩次write,報出SIGPIPE訊號,導致異常。
    由於只有ios裝置有該問題,故可能性較低。

  3. 可能性三:
    請求介面本身問題或併發呼叫問題,引起了連線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分析
  1. 可能性一:
    處理報文過長,client端無法解析/處理過大報文,導致本次及下次請求。
    客戶端系統對於大報文的處理問題需要測試和調研,故有可能性。

  2. 可能性二:
    處理時間過長,導致當client端併發請求時,當上次請求尚未完成,下次請求會close上次請求,以本次為準。
    調研後發現client端是阻塞請求,但需要進行實際測試,故有可能性。

  3. 可能性三:
    處理時間過長,在握手過程中client自行中斷了連線。
    由於執行時間較長,頻率較高,客戶或裝置自己可能觸發kill程序或關閉連線等操作,故有可能性。

3.測試過程

3.1.場景設計

  1. 服務端:
    返回引數量大、處理時間長的介面
  2. 客戶端:
    h端客戶端;大量輸入引數;併發呼叫介面的工具
  3. 介面:
    URL:/im/getTests.json
    入參:emNames (List,每條約24字元)
    出參:PageForm
    邏輯:根據emNames數量,組裝並返回同等數量的PageForm(每條約250字元),根據需求sleep若干時間(3000-5000ms)。

3.2.測試報告

3.2.1.IOS
  1. ios,4000引數,3次併發,服務端無sleep: 無問題
    這裡寫圖片描述

  2. ios,4000引數,3次併發,服務端sleep 3000ms: 無問題
    這裡寫圖片描述

  3. ios,2000引數,20次併發,服務端sleep 5000ms,中途client切出: 無問題
    這裡寫圖片描述

  4. ios,2000引數,20次併發,服務端sleep 5000ms,中途client中斷: 發現問題,完全符合異常現象
    這裡寫圖片描述
    這裡寫圖片描述

3.2.2.ANDROID
  1. android,2000引數,10次併發,服務端sleep 5000ms,中途client中斷:發現問題,完全符合異常現象
    這裡寫圖片描述
    這裡寫圖片描述

4.結論

4.1.問題原因

client端使用者在殺死程序時,介面的TCP請求尚未完成(未完成的原因是處理時間長)。
導致server端write資料時,收到SIGPIPE訊號,丟擲Broken pipe異常。
但由於已經殺死了程序,並不會對使用者產生任何影響。

4.2.其他結論

  1. ios和android在處理1M以內(尚不清楚有沒有最大值)大小報文時,沒有問題
  2. ios和android均是阻塞請求,本次請求不會對上次請求造成影響,即使上次請求尚未完成
  3. ios切換至後臺,短時間內(5000ms以內,最大值根據系統不同而不同)並不會中斷已經存在的TCP連線。
  4. android切換至後臺,並不會中斷已經存在的TCP連線。
  5. ios、android殺死程序會一併關閉已經存在的TCP連線。
  6. android框架(公司基於google改造的框架)預設併發5執行緒一組。

5.解決方案

由於kill程序我們無法控制,故只能通過降低介面處理時間,減少使用者kill程序時未完成的TCP連線數量。

具體:

  1. 會話列表翻頁
  2. 會話列表限制展示數量
  3. 客戶端分組獲取會話列表資料