天天看點

springcloudAlibaba+dubbo線程擁堵Thread pool is EXHAUSTED

問題原因

1.由于dubbo服務的負載模式是輪詢模式,導緻每台機器上配置設定的任務數量是基本上相同的,但是由于服務部署并不是單機部署的(一台機器上面部署了多個服務),導緻有些機器處理的速度較快,有些機器處理的較慢

2.由于dubbo的業務線程池設定的預設核心線程數量為200,線程池類型為fixed,并且線程隊列為0(設定隊列為0,目的也是為了防止隊列堆積任務過多,導緻上遊調用逾時),是以當機器處理任務緩慢時,任務一直占用線程未釋放就會導緻任務占用線程越來越多,最終導緻線程池打滿的問題。

解決過程

初步解決方案:增大線程數,增加執行個體數量(部署多個執行個體)

最開始,由于出現的問題是線程池被打滿了,那麼增大核心線程數量,減少每台機器的任務數量(增加執行個體)是不是可以解決問題呢,于是增加了每個dubbo服務的核心線程數量和部署的執行個體數量。發現增加到一定程度後确實可以解決dubbo線程池打滿的問題,但是通過觀察機器情況會發現有些機器處理任務的速度比較快,有些機器處理的則比較慢。于是進一步去分析機器情況,發現有些機器的cpu和記憶體存在基本打滿的一個狀态,于是就把部署在機器情況不是很良好的服務遷移到資源比較充足的機器上面,于是情況有了進一步改善。通過一系列操作後發現無腦增加線程數量可能并不是一個好的解決方案,因為更多線程數量意味着需要更多的記憶體(每個線程占用記憶體的大小取決于jvm參數中xss對每個線程棧大小的設定)和cpu切換的消耗。于是,接下來變嘗試減小部署服務執行個體數和減少線程數。

改進方案:單機單執行個體部署,dubbo線程優化

既然多個服務部署在一台機器上會互相影響各個服務的機器資源配置設定問題,改成單機單執行個體呢。于是我們購買了幾台8c16G的機器單獨部署目标服務,發現情況有了進一步改善。單機單執行個體部署後進一步觀察機器情況,發現随着機器能力的提升,任務處理速度和線程數量有了明顯的降低(處理速度快,相應的就不會建立更多的線程)。于是發現可能服務根本用不了那麼多線程,于是接下來開始對dubbo業務線程做進一步研究,看看有沒有什麼提升的地方。于是開始對dubbo業務線程模型進行分析結果如下:

  • 如果事件處理的邏輯能迅速完成,并且不會發起新的IO請求,比如隻是在記憶體中記個辨別。則直接在IO線程上處理更快,因為減少了線程池排程。
  • 但如果事件處理邏輯較慢,或者需要發起新的IO請求,比如需要查詢資料庫,則必須派發到線程池,否則IO線程阻塞,将導緻不能接收其他請求。
  • 如果用IO線程處理事件,又在事件處理過程中發起新的IO請求,比如在連接配接事件中發起登入請求,會報“可能引發死鎖”異常,但不會真死鎖。

對于Dubbo叢集中的Provider角色,有IO線程池(預設無界)和業務處理線程池(預設200,有界隊列,隊列大小為0)兩個線程池,是以當業務的并發比較高,或者某些業務處理變慢,而且此時我們沒給Provider的線程池配置等待Queue,業務線程池就很容易被“打滿”,抛出“RejectedExecutionException: Thread pool is EXHAUSTED! ”異常。

既然Provider端已經抛出異常,表明自己已經受不了了,但線上我們卻發現,Consumer無動于衷,發出的那筆請求還在一直等待直到逾時。這樣極其容易導緻整個系統的“雪崩”,因為它違背了fail-fast原則。我們希望一旦Provider由于線程池被打滿而無法收到請求,Consumer應該立即感覺然後快速失敗來釋放線程。這是因為Dispatcher配置得不對,預設是all,我們應該配置成message。Dispatcher是dubbo線程池的分發政策:

all:所有消息都派發到線程池,包括請求,響應,連接配接事件,斷開事件,心跳等。

direct:所有消息都不派發到線程池,全部在IO線程上直接執行。

message:隻有請求響應消息派發到線程池,其他連接配接斷開事件,心跳等消息,直接在IO線程上執行。

execution:隻有請求消息派發到線程池,不含響應,響應和其他連接配接斷開事件,心跳等消息,直接在IO線程上執行。

connection: 在IO線程上,将連接配接斷開事件放入隊列,有序逐個執行,其他消息派發到線程池。

于是我們根據業務類型将dubbo線程分發政策改為message,發現服務性能有了進一步提升,于是既然分發政策進行修改了,那麼是不是可以對dubbo線程池類型做進一步優化呢?于是對dubbo中業務線程的類型做了進一步研究:dubbo線程池類型有:

fixed:固定大小的線程池,啟動時建立線程,不關閉,一直持有。(預設)

cached:緩存線程池,空閑一分鐘自動删除,需要時重建。

limited:可伸縮線程池,但翅中線程數量隻增長不會收縮。(為了避免大流量引起的性能問題)。

于是通過分析:我們将線程池類型切換為cached,因為我們業務類型為直播相關業務,有高峰期和空閑期,并且cached線程一分鐘才會自動删除。再高峰期時也不會出現頻繁重建的問題,并且相較于固定線程來索能減少對機器資源的消耗。

經過一系列改善後發現服務性能有了明顯的提升。如果想做進一步的提升可以通過rpc通訊性能方面下手,這邊給各位網友分享一遍網友寫的關于rpc通訊協定性能的文章連結基于dubbo架構下的RPC通訊協定性能測試 - lengfo - 部落格園

最後歡迎各位碼友評論區交流溝通