記幾次 [線上環境] Dubbo 線程池佔滿原因分析(第一次:HttpClient)

轉載:https://blog.csdn.net/wsmalltiger/article/details/124236055

前言
  我們一個核心應用,線上部署了4臺機器(4c8g),某天晚上8點左右線上忽然出現dubbo線程池佔滿告警,上游應用error日誌也瘋狂報警,整個過程持續了4分鐘左右系統自動恢復正常。
  dubbo 默認200個線程池,報錯日誌信息:

03-26 20:22:32.740 ERROR 25110 --- [ThreadPoolMonitor-thread-1] c.*.*.c.scheduled.ScheduledContainer : [DUBBO] Pool status:OK, max:200, core:200, largest:200, active:0, task:260504, service port: 20881;Pool status:WARN, max:200, core:200, largest:200, active:200, task:5568440, service port: 20882, dubbo version: 3.2.3.8-RELEASE, current host: 10.*.*.7
03-26 20:23:32.740 ERROR 25110 --- [ThreadPoolMonitor-thread-1] c.*.*.c.scheduled.ScheduledContainer : [DUBBO] Pool status:OK, max:200, core:200, largest:200, active:0, task:261272, service port: 20881;Pool status:WARN, max:200, core:200, largest:200, active:200, task:5573570, service port: 20882, dubbo version: 3.2.3.8-RELEASE, current host: 10.*.*.7
03-26 20:24:32.740 ERROR 25110 --- [ThreadPoolMonitor-thread-1] c.*.*.c.scheduled.ScheduledContainer : [DUBBO] Pool status:OK, max:200, core:200, largest:200, active:0, task:262550, service port: 20881;Pool status:WARN, max:200, core:200, largest:200, active:200, task:5578245, service port: 20882, dubbo version: 3.2.3.8-RELEASE, current host: 10.*.*.7
03-26 20:25:32.740 ERROR 25110 --- [ThreadPoolMonitor-thread-1] c.*.*.c.scheduled.ScheduledContainer : [DUBBO] Pool status:OK, max:200, core:200, largest:200, active:0, task:264110, service port: 20881;Pool status:WARN, max:200, core:200, largest:200, active:200, task:5582899, service port: 20882, dubbo version: 3.2.3.8-RELEASE, current host: 10.*.*.7

一、問題分析
1、查看監控
  公司的中間件監控系統做的比較完善,對dubbo框架的監控有進行定製化開發,並集成到統一運維平臺,能夠更加方便開發同學瞭解服務運行情況,出現問題時系統整體QPS流量:

對比前一天的系統QPS流量:

問題當天整體dubbo服務監控信息:

從上面監控信息中可以得出三個結論:
1、問題當天系統QPS流量開始相對平穩,下降的波動主要是由於問題期間dubbo線程池被佔滿導致無法處理其他請求,上升的波動是系統自動恢復後流量統計到同一時間上了,整體流量相比前一天流量沒有明顯增長;
2、Provider(服務端)平均RT上升較大,問題期間dubbo出現線程被長時間佔用問題;
3、在RT上升機器,Consumer、Provider都有大量失敗;

2、找到問題接口
查看dubbo接口RT監控,發現有一個接口問題期間平均RT達到 31秒多:

這麼高的RT說明接口內部肯定出現了問題,繼續查看接口RT耗時明細,最高RT將近達到 80秒:


3、分析接口
既然找到的問題接口,那麼查看一下接口的日誌信息,看看能否定位到原因:

  日誌使用spring AOP做的切面,針對系統接口耗時如果超過3秒,則會自動打印出固定格式的日誌,方便後續對高RT接口進行優化。這裏AOP的好處一下就表現出來了,根據日誌輕鬆找到相關信息,繼續正對一條請求查看日誌明細:

看到這個日誌原因基本就可以確定了:dubbo接口內部請求了某個http接口,而這個http接口耗時較長導致長時間佔用dubbo線程不釋放,最終引起dubbo線程池被佔滿。

4、問題驗證
  爲什麼http請求超時會導致dubbo線程長時間被佔用呢?通過review接口代碼,發現應用中使用了org. apache. http. impl. client. HttpClients工具類發送 http請求,繼續 review 開源工具源碼發現這個工具默認僅支持5個併發:

編寫測試代碼,驗證一下超時是怎麼形成的(爲了模擬問題時的場景,請求的http接口內部會阻塞2秒):

for (int i = 0; i < 100; i++) {
new Thread(()->{
long tempTime = System.currentTimeMillis();
try {
String code = restfulServiceClient.get("http://10.*.*.88:7001/account/get?bId=" + bId + "&buyerId=" + buyerId, String.class);
} catch (Exception e) {
logger.error("請求超時:" , e);
}
long useTime = System.currentTimeMillis() - tempTime;
if (useTime < 1000 * 4) {
logger.info("i=" + index.incrementAndGet() + " 單次耗時:{} ms", useTime);
} else {
logger.error("i=" + index.incrementAndGet() + " 超時,單次耗時:{} ms", useTime);
}
}).start();
}


查看輸出日誌:

   這下原因清楚了:由於http工具默認僅支持 5 個併發,且有線程池隊列,當請求量超過 5 個的時候,多餘的請求會在隊列中堆積。前一批http請求結束之後其他的請求才會繼續執行,越到後面線程等待時間會越長。所以對應實際業務場景中dubbo線程等待的時間也會越長,當這個隊列到一定數量之後,就會引起dubbo默認200個線程池被佔滿的情況,從而引起整個應用服務的報錯。

二、解決方案
知道了問題原因,解決方案就變得簡單起來:
1、系統中依賴 http 接口遷移到 java dubbo接口(對應業務方已經有相關dubbo接口);
2、工具類封裝並進行優化,調整到合適的併發執行數量,修改隊列方式,設置超時超時時間(參數值根據業務場景具體分析);
3、問題接口接入限流降級框架,配置限流閾值,防止在高流量的情況下導致接口被打掛;

總結
1、使用開源框架時要了解相關的原理,默認值不一定適合所有的場景,需要根據自己的場景分析選擇對應的配置。
2、對代碼存在敬畏之心,複雜的業務代碼中,一個看似不起眼的http請求出現問題,最終會導致整個dubbo服務不可用。
3、完善監控系統,及時主動發現問題,及時處理降低影響面。


————————————————
版權聲明:本文爲CSDN博主「smatiger」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wsmalltiger/article/details/124236055

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章