dubbo傳送過程編碼失敗,會喚醒傳送(客戶端業務)執行緒嗎?如何實現的?
在上篇文章 dubbo坑- No provider available for the service xxx 中,如果dubbo請求階段,編碼異常,而業務執行緒依然在等待響應,dubbo如何處理的?總不能等待超時,響應個超時異常吧,這不合理,接下來看dubbo編碼異常,如何處理的
回顧下之前自己分析的dubbo transport層記錄,詳細記錄了dubbo呼叫鏈路(包括netty channelhandler chain,dubbo channelhandler chain)的執行,在簡單回顧下:
客戶端傳送:此時是在業務執行緒執行,客戶端業務執行緒依次執行 InvokerInvocationHandler入口 -> RegistryDirectory獲取服務集合 -> tagRouter過濾 -> 負載均衡選取一個Invoker -> dubbo consumer filter chain -> DubboInvoker執行呼叫invoke -> Exchange層封裝請求模型Request且建立DefaultFuture並阻塞等待 -> 網路客戶端NettyClient傳送訊息 -> dubbo Channel即NettyChannel傳送 -> 呼叫netty channle傳送 io.netty.channel.Channel.writeAndFlush(message),接著觸發執行netty pipeline的outbound事件,執行順序TailContext->NettyClientHandler->InternalEncoder->InternalDecoder->HeadContext,其中在TailContext內由業務執行緒切換到reactor IO執行緒,接著InternalEncoder進行編碼,最終由HeadContext把資料傳送給服務方;
客戶端接收服務端響應:reactor IO執行緒監聽到selector有read事件,執行processSelectedKeys(),觸發執行netty pipeline的inbound事件,執行順序【HeadContext->InternalDecoder->InternalEncoder->NettyClientHandler->TailContext】,由InternalDecoder解碼,接著在NettyClientHandler#channelRead執行,NettyClientHandler持有dubbo channelhandler chain,因此鏈式執行dubbo channelhandler chain,即NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->DubboProtocol$1,這裡重要的是在AllChannelHandler內由IO執行緒切換到業務執行緒(封裝響應Response為ChannelEventRunnable,提交到dubbo業務執行緒池),接著在業務執行緒上HeaderExchangeHandler判斷mesage是Response,處理響應,喚醒DefaultFuture的阻塞等待。
接著客戶端業務執行緒被喚醒,根據Response內容處理,獲取返回的資料結果/異常。
以上就是dubbo整個傳送-響應的整個流程,重點是netty pipeline和dubbo channelhandler chain,都是責任鏈模式增加功能。
接著分析我們這個問題,從上面回顧的流程可知,未實現序列化,那麼異常肯定發生在dubbo編碼階段,即InternalEncoder丟擲異常,異常是java.lang.RuntimeException,異常資訊Serialized class com.zzz.ioc.codec.util.KeyValuePair must implement java.io.Serializable Java field: private com.zzz.ioc.codec.util.KeyValuePair com.zzz.ioc.protocol.t808.T0900.message
,異常封裝在netty DefaultChannelPromise,因此在NettyClientHandler獲取InternalEncoder.write結果(此過程在IO執行緒執行),判斷是否有無異常,程式碼如下
訊息傳送有異常,則mock response返回,接著執行NettyClientHandler持有的dubbo channelhandler chain[NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->DubboProtocol$1],AllChannelHandler執行,IO執行緒切換到業務執行緒(dubbo客戶端執行緒池,執行緒名稱DubboClientHandler-開頭),接著業務執行緒上執行HeaderExchangeHandler,處理response,喚醒傳送等待執行緒。此過程為了避免write操作失敗,mock Request,然後和正常接收響應基本一樣的處理方式(不同之處是不經過selector的processSelectedKeys())喚醒傳送執行緒。通訊框架出現這個問題也不容易,但是dubbo設計的非常巧妙。
小結:
回答前面提問
1.dubbo傳送過程編碼失敗,會喚醒傳送執行緒嗎?
會喚醒傳送執行緒,否則傳送執行緒就是timeout異常,實際並沒有。
如何實現的?
傳送異常,比如編碼異常,NettyClientHandler 進行mock response,接著和正常處理響應基本相同方式,觸發NettyClientHandler 持有的dubbo channelhandler chain,由AllChannelHandler封裝響應Response為ChannelEventRunnable,提交到dubbo業務執行緒池(cache執行緒池,執行緒名DubboClientHandler-
開頭),執行緒由IO執行緒切換到dubbo業務執行緒執行Exchange,即HeaderExchangeHandler處理response,繼而喚醒傳送執行緒,最後傳送(客戶端業務)執行緒處理響應業務邏輯。