背景: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()方法,因此由NettyHandler的messageReceived()方法處理
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,因此打印了一句日誌,而後就返回了,也就是不處理了。。
而後就沒而後了,這個請求就這樣被沉入大海了,客戶端沒有收到任何響應。。。
本次分析就完全結束了,客戶端超時,真相大白。