dubbo服務端線程池溢出,異常客戶端無響應超時-Dubbo,Netty代碼解讀

背景:this

客戶端調用dubbo服務端,dubbo服務端線程溢出,客戶端沒法收到響應,請求超時spa

 

前提:線程

建議打開netty3.7.0Final代碼,dubbox2.8.4代碼,邊讀邊看代碼。3d

 

關鍵信息:日誌

類:netty

ChannelUpstreamHandlercode

請求消息流進NettyServer端(當前代碼重點關注)blog

ChannelDownstreamHandler繼承

NettyServer端響應返回(當前代碼分析不涉及)接口

ChannelPipeline

NettyServer解析數據的職責鏈

ChannelHandler

消息處理器(AllHandler)

Sink

消息最後一步處理

 

netty事件

MessageEvent:普通請求消息事件

ExceptionEvent:異常事件

 

方法:

sendUpStream()

流入信息總入口

handleUpstream()

處理流入信息

received()

消息事件處理

caught()

異常事件消息

 

先分析下NettyServer的構造,此處爲靜態信息(信息尚未流入進來)

編解碼channelHandler

NettyServer -> pipeline.addLast("decoder", adapter.getDecoder());

NettyServer -> pipeline.addLast("encoder", adapter.getEncoder());

 

核心處理業務channelHandler

final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);

NettyServer -> pipeline.addLast("handler", nettyHandler);

將NettyHandler增長到nettyServer的職責鏈中,處理請求消息

 

Handler類繼承關係:

NettyHandler ->SimpleChannelHandler -> ChannelUpstreamHandler(處理流入消息總接口)->ChannelHandler(頂級接口sendUpstream()方法)

 

NettyServer會按照pipline職責鏈執行每一個ChannelHandler的sendUpstream()方法,ChannelUpstreamHandler實例的handleUpstream方法

因此此處 sendUpstream() 做爲全部請求消息總入口,後面的分析均今後接口開始。

 

如下開始分析消息流入處理流程:

1.流入一個dubbo請求信息

DefaultChannelPipeline.sendUpstream()職責鏈驅動

核心代碼在此,調用handler的handlerUpstream()(能夠理解爲reveived),若是received處理異常,則執行notifyHandlerException()

 

2.SimpleChannelHandler.handleUpstream()執行職責單元中的方法

 

3.消息類型爲MessageEvent,交給messageReceived()方法處理, NettyHandler實現了messageReceived()方法,因此由NettyHandlermessageReceived()方法處理

NettyHandler.messageReceived(ctx, (MessageEvent)e);

 

4.messageReceived方法內調用handler.received()方法,

this.handler.received(channel, e.getMessage());

此時的handler使用裝配器模式,已經變成了AllChannelHandler(主角出場了)

 

5.AllChannelHandler因爲線程池溢出,致使執行失敗,拋出異常

 

6.按調用鏈層層返回,則返回到DefaultChannelPipeline.sendUpstream()中,執行notifyHandlerException()方法

 

7.繼續跟蹤notifyHandlerException()方法

關注兩個點:

1)當前事件仍是MessageEvent,因此不走第一個if

2)調用sink.exceptionCaught()方法處理異常,此處的sink是誰呢????

 

8.回看一下NettyServer裏邊是否認義了sink??(sink是pipline中的最後一個元素)

好像沒有定義sink????

咱們再看下第一個紅框中的代碼,這裏new了一個ChannelFactory,看看裏邊有什麼玄機??

層層跟進,找到根構造方法,sink終於現身了 NioServerSocketPipelineSink繼續看 NioServerSocketPipelineSink中的exceptionCaught()方法中幹了啥??

很失望,NioServerSocketPipelineSink沒有實現exceptionCaught()方法,那就找父類,最終找到了實現了exceptionCaught()方法的實例 AbstractChannelSink

 

代碼好多,繼續深刻,當前執行線程是iothreads

看一下這個if判斷裏作了啥???

AbstractNioChannelSink重寫了AbstractChannelSink的該方法,此處該方法,應該返回false

如今代碼執行到了fireExceptionCaught,繼續跟蹤

向管道里推了一個異常事件sendUpstream(異常事件),繼續跟蹤,代碼執行和前面一些步驟同樣,只是事件類型變動了,因此咱們只看關鍵代碼

 

跟蹤到SimpleChannelHandler的handleUpstream方法,此次,走異常事件流程,執行exceptionCaught(異常事件)方法

一樣,NettyHandler實現了exceptionCaught(異常事件)方法,代碼以下

敲黑板,重點重點!!!,此時能夠理解AllChannelHandler要執行caught方法了,截止到目前,終於跟蹤到了AllChannelHandler第一次線程溢出異常,調用了本身的caught方法,基本找到了dubbo在何處調用caught方法的,本次分析任務就要結束了??

 

第二大段:

本着打破砂鍋問到底的鑽研精神,這個時候,若是dubbo處理了這個異常,那消費者應該會收到一個失敗或者異常,消費者就會處理失敗消息,可是現狀爲什麼是消費者由於沒有收到響應而超時呢????

繼續跟蹤

此時,執行AllChannelHandler的caught方法,會不會有問題呢? 實際狀況是,確實有問題,因爲業務線程池滿了AllChannelHandler的caught方法繼續執行失敗。。啥?又拋異常了??!!!

我們再次回到,sendUpstream()方法,看看系統如何處理二次異常??

鞏固記憶,入口圖,再貼一次

繼續跟蹤第二次異常處理流程

和第一次處理異常流程不同了,此時的ChannelEvent爲ExceptionEvent,因此打印了一句日誌,而後就返回了,也就是不處理了。。

而後就沒而後了,這個請求就這樣被沉入大海了,客戶端沒有收到任何響應。。。

 

本次分析就完全結束了,客戶端超時,真相大白。

相關文章
相關標籤/搜索