1、問(wèn)題背景:
應(yīng)用在配合R2m升級(jí)redis版本的過(guò)程中,上游反饋調(diào)用接口報(bào)錯(cuò),RpcException:[Biz thread pool of provider has been exhausted],通過(guò)監(jiān)控系統(tǒng)和日志系統(tǒng)定位到現(xiàn)象只出現(xiàn)在一兩個(gè)節(jié)點(diǎn),并持續(xù)出現(xiàn)。第一時(shí)間通過(guò)JSF將有問(wèn)題的節(jié)點(diǎn)下線,保留現(xiàn)場(chǎng),業(yè)務(wù)恢復(fù)。
報(bào)錯(cuò)日志如下:
24-03-13 02:21:20.188 [JSF-SEV-WORKER-57-T-5] ERROR BaseServerHandler - handlerRequest error msg:[JSF-23003]Biz thread pool of provider has been exhausted, the server port is 22003 24-03-13 02:21:20.658 [JSF-SEV-WORKER-57-T-5] WARN BusinessPool - [JSF-23002]Task:com.alibaba.ttl.TtlRunnable - com.jd.jsf.gd.server.JSFTask@0 has been reject for ThreadPool exhausted! pool:80, active:80, queue:300, taskcnt: 1067777
2、排查步驟:
從現(xiàn)象開始推測(cè)原因,系統(tǒng)啟動(dòng)時(shí),會(huì)給JSF線程池分配固定的大小,當(dāng)線程都在工作的時(shí),外部流量又打進(jìn)來(lái),那么會(huì)沒(méi)有線程去處理請(qǐng)求,此時(shí)會(huì)有上述的異常。那么JSF線程在干什么呢?
1)借助SGM打印棧信息
2)分析棧信息
可以用在線分析工具:http://spotify.github.io/threaddump-analyzer/?
2.1)分析線程狀態(tài)
通過(guò)工具可以定位到JSF線程大部分卡在JedisClusterInfoCache#getSlaveOfSlotFromDc方法,如圖:
2.2)分析線程夯住的方法
getSlaveOfSlotFromDc在方法入口就需要獲取讀鎖,同時(shí)在全局變量聲明了讀鎖和寫鎖:
此時(shí)對(duì)問(wèn)題有一個(gè)大體的了解,大概推測(cè):getSlaveOfSlotFromDc是獲取redis連接池,該方法入口處需要獲取讀鎖,由于讀鎖之間不會(huì)互斥,所以猜測(cè)有業(yè)務(wù)獲取到寫鎖后沒(méi)有釋放。同時(shí)讀鎖沒(méi)有設(shè)置超時(shí)時(shí)間,所以導(dǎo)致杰夫線程處理業(yè)務(wù)時(shí)卡在獲取讀鎖處,無(wú)法釋放。
2.3)從業(yè)務(wù)的角度分析持有寫鎖的邏輯
向中間件研發(fā)尋求幫助,經(jīng)過(guò)排查,定位到有個(gè)更新拓?fù)涞亩〞r(shí)任務(wù),執(zhí)行時(shí)會(huì)先獲取寫鎖,根據(jù)該消息,定位到任務(wù)的棧信息:
代碼截圖:
圖1
圖2
圖3
從日志驗(yàn)證:日志只打印更新拓?fù)涞娜罩荆瑳](méi)有打印更新成功的日志,且02:20分以后r2m-topo-updater就不在打印日志
2.4)深入挖掘原因
雖然現(xiàn)象已經(jīng)可以推測(cè)出來(lái),但是對(duì)問(wèn)題的原因還是百思不得其解,難道parallelStream().forEach存在bug?難道有遠(yuǎn)程請(qǐng)求,沒(méi)有設(shè)置超時(shí)時(shí)間?...
經(jīng)過(guò)查找資料確認(rèn),如果沒(méi)有指定,那么parallelStream().forEach會(huì)使用ForkJoinPool.commonPool這個(gè)默認(rèn)的線程池去處理任務(wù),該線程池默認(rèn)設(shè)置(容器核心數(shù)-1)個(gè)活躍線程。同時(shí)caffeine數(shù)據(jù)過(guò)期后會(huì)異步刷新數(shù)據(jù),如果沒(méi)有指定線程池,它默認(rèn)也會(huì)使用ForkJoinPool.commonPool()來(lái)執(zhí)行異步線程。那么就有概率出現(xiàn)獲取到寫鎖的線程無(wú)法獲取執(zhí)行權(quán),獲取執(zhí)行權(quán)的線程無(wú)法獲取到讀鎖。
2.5)驗(yàn)證
3個(gè)ForkJoinPool.commonPool-worker的確都夯在獲取redis連接處,線程池的活躍線程都在等待讀鎖。
本地caffeine緩存沒(méi)有設(shè)置自定義線程池
topo-updater夯在foreach業(yè)務(wù)處理邏輯中
3.復(fù)盤
1)此問(wèn)題在特定的使用場(chǎng)景下才會(huì)小概率出現(xiàn),非常感謝中間件團(tuán)隊(duì)一起協(xié)助定位問(wèn)題,后續(xù)也將異步更新拓?fù)涓臑橥教幚怼?/p>
2)Java提供了很多異步處理的能力,但是異常處理也代表需要開啟線程或者使用共用的線程池,也需要注意。
3)做好監(jiān)控,能第一時(shí)間發(fā)現(xiàn)問(wèn)題并處理問(wèn)題。
審核編輯 黃宇
-
接口
+關(guān)注
關(guān)注
33文章
8499瀏覽量
150837 -
JSF
+關(guān)注
關(guān)注
0文章
11瀏覽量
7742
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論