dubbo集羣中的provider角色,有兩個線程池,一個是IO線程池,一個是業務線程池(默認200) 當業務線程併發比較高,或者業務處理變慢,業務線程池很容易滿額,拋出「RejectedExecutionException: Thread pool is EXHAUSTED! 」異常。固然,前提是咱們每給Provider的線程池配置等待Queue(隊列)。 既然provider端已經拋出異常,表示本身受不了,可是consumer無動於衷,發出的請求還在等待,直到超時。這樣極其容易致使整個系統的「雪崩」,由於它違背了fail-fast原則。 咱們但願一旦Provider因爲線程池被打滿而沒法收到請求,Consumer應該當即感知而後快速失敗來釋放線程。後來發現,徹底是Dispatcher配置得不對,默認是all,咱們應該配置成message。 調度器 Dispatcher 調度策略緩存
線程池 ThreadPool fixed 固定大小線程池,啓動時創建線程,不關閉,一直持有。(缺省) cached 緩存線程池,空閒一分鐘自動刪除,須要時重建。 limited 可伸縮線程池,但池中的線程數只會增加不會收縮。只增加不收縮的目的是爲了不收縮時忽然來了大流量引發的性能問題。 eager 優先建立Worker線程池。在任務數量大於corePoolSize可是小於maximumPoolSize時,優先建立Worker來處理任務。當任務數量大於maximumPoolSize時,將任務放入阻塞隊列中。阻塞隊列充滿時拋出RejectedExecutionException。(相比於cached:cached在任務數量超過maximumPoolSize時直接拋出異常而不是將任務放入阻塞隊列) 從上面能夠看到全部的請求、響應、鏈接、斷開都通過Dispatcher之手。 每種Dispatcher,都有對應的ChannelHandler,ChannelHandler將Handler的調動造成調用鏈。若是配置的是all,那麼接下來上場的就是AllChannelHandler;若是配置的是message,那麼接下來上場的就是MessageOnlyChannelHandler,這些ChannelHandler都是WrappedChannelHandler的子類,WrappedChannelHandler默認把請求、響應、鏈接、斷開、心跳操做都交給Handler來處理。 AllChannelHandler覆蓋了WrappedChannelHandler全部的關鍵操做,都將其放進到ExecutorService(這裏指的是業務線程池)中異步來處理,但惟一沒有異步操做的就是sent方法,該方法主要用於應答,但官方文檔卻說使用all時應答也是放到業務線程池的,寫錯了?這裏,關鍵的地方來了,一旦業務線程池滿了,將拋出執行拒絕異常,將進入caught方法來處理,而該方法使用的仍然是業務線程池,因此頗有可能這時業務線程池仍是滿的,因而悲劇了,直接致使下游的一個HeaderExchangeHandler沒機會調用,而異常處理後的應答消息正是HeaderExchangeHandler#caught來完成的,因此最後NettyHandler#writeRequested也沒有被調用,Consumer只能死等到超時,沒法收到Provider的線程池打滿異常。 MessageOnlyChannelHandler只覆蓋了WrappedChannelHandler的received方法,意味着只有請求處理會用到業務線程池,其餘的非業務操做直接在IO線程池執行,這不正是咱們想要的嗎?因此使用message的Dispatcher,不會存在Provider線程池滿了,Consumer卻還在傻等的狀況,由於默認IO線程池是無界的,必定會有線程來處理異常和應答(若是你把它設置成有界,那我也沒啥好說的了)。併發