天天看點

Dubbo概述--調用過程Dubbo概述–調用過程

Dubbo概述–調用過程

  • Dubbo概述–調用過程
    • 寫在前面
    • 調用過程
      • Consumer請求
      • Provider響應
      • Consumer接收響應
    • 總結

寫在前面

本文參考了Dubbo官方手冊結合Dubbo2.6.1版本源碼分析。推薦先閱讀官方手冊。

鑒于個人水準有限,如有不正确的地方請指出,歡迎一起讨論,謝謝!

調用過程

本過程的分析是基于最簡單的Demo進行分析的,主要的目的是介紹架構和流程。

Consumer請求

Dubbo概述--調用過程Dubbo概述–調用過程
  1. Consumer調用ProxyFactory擴充類生成的代理類:ProxyN中的方法。
    • ProxyFactory擴充點的預設類為JavassistProxyFactory
    • ProxyN中的N代表本次啟動建立的第N個代理,從0開始的AtomicLong靜态變量
  2. ProxyN類内部将調用方法轉化為調用InvokerInvocationHandler提供的通用方法:invoke(obj, methodName, args)。
  3. InvokerInvocationHandler将傳入的methodName、args構造為RPCInvocation執行個體,并調用MockClusterInvoker的invoke(Invocation)方法。
    • RPCInvocation執行個體可以了解為整個傳輸過程中的資料載體類,存儲調用方法、參數類型、參數等
  4. MockClusterInvoker根據Directory的URL确定調用方法時是否需要、怎樣調用Mock,如果不是強制使用Mock則調用FailoverClusterInvoker的invoke方法。
    • MockClusterInvoker是通過服務擴充功能中自動添加包裝類(Cluster的包裝類MockClusterWrapper)的機制添加進來的
    • 沒有methodName.mock參數則直接向下調用
    • methodName.mock參數值以force開始時,直接使用mock而不會調用Provider
    • methodName.mock參數值為其他時,隻有調用Provider抛異常時才使用mock
    • 相關的MockInvokersSelector是通過寫死AbstractDirectory.setRouters添加到Directory的
  5. FailoverClusterInvoker根據自身邏輯以及Directory、Router、LoadBalance的實作類确定具體調用Invoker。本例中為InvokerDelegate。關于Cluster、Direcotry、Router、LoadBalance請參見叢集相關。
    • FailoverClusterInvoker是Cluster擴充點的預設類
  6. InvokerDelegate存儲provider的URL,然後調用ProtocolFilterWrapper
  7. ProtocolFilterWrapper将Filter擴充點的實作類中@Activate注解的group值為consumer的Filter排序後組成過濾鍊,并逐個調用Filter,然後調用ListenerInvokerWrapper。
    • Filter鍊中的Filter根據@Activate注解的before、after、order屬性排序的
  8. ListenerInvokerWrapper中通知InvokerWrapper的監聽器。然後調用DubboInvoker。
  9. DubboInvoker根據URL中的參數決定是同步、異步還是通知調用,進而調用ReferenceCountExchangeClient發送請求。
    • DubboInvoker在執行個體化時會根據URL建立與Provider的連接配接對象并連接配接Provider。
  10. ReferenceCountExchangeClient調用實際的Client:HeaderExchangeClient進行調用。
    • ReferenceCountExchangeClient執行個體是Consumer使用共享connection通路多個Provider時的預設Client,用于一會用計數。
  11. HeaderExchangeClient是預設的用戶端,會向Provider發送心跳。并調用HeaderExchangeChannel發送調用資訊(Invocation)。
  12. HeaderExchangeChannel調用内部的Client接口的實作類發送調用,同時将發送的資訊儲存到DefaultFuture中,供後續接收響應時根據對應關系傳回給Consumer上次調用。
  13. 預設使用NettyClient(非最新的netty4下邊的包)作為Client發送調用,Client使用自身持有的Channel來發送資料。
    • 這裡的Client接口是根據Transporter擴充接口指定傳輸器
    • Client接口相關的編碼器為Codec2擴充接口指定
    • Code2中的序列化方式使用Serialization擴充接口指定
  14. 為了對多種傳輸方式擴充進行統一的抽象,這裡的Channel是對Dubbo自身的Channel,對各種具體(例如Netty的Channel)進行封裝。最終調用Netty中的channel.write發送。
  15. 雖然請求是也會觸發一系列的handler,但是這些handler不會控制發送資料,隻是調用。這點不确定是否正确。

Provider響應

Dubbo概述--調用過程Dubbo概述–調用過程
  1. Netty低層接收到位元組,并通過InternalEncoder類将位元組解碼為Java對象(Request),然後通過NettyHandler調用NettyServer的received方法。
    • InternalEncoder以及InternalDecoder會根據編碼器接口Codec2進行編碼
    • Code2中的序列化方式使用Serialization擴充接口指定
  2. NettyServer的received方法存在于父類AbstractPeer中,AbstractPeer是Server、Client的公共父類,用于調用内部的handler(MultiMessageHandler)進行後續處理。
    • NettyServer(Provider)和NettyClient(Consumer)都會使用MultiMessageHandler->HeartbeatHandler->從Dispatcher擴充點擷取的ChannelHandler。
  3. MultiMessageHandler判斷解碼後的調用資訊是否是MultiMessage的執行個體,根據判斷結果調用内部的handler(HeartbeatHandler)。
    • 如果是MultiMessage,則疊代調用其内部的消息。否則直接調用消息。
  4. HeartbeatHandler用于專門用于處理心跳請求,非心跳請求才會繼續調用後續的handler(dispatcherHandler)。
    • 對于心跳請求,設定讀時間戳,并根據請求中的twoway屬性确定是否需要傳回響應
    • 對于心跳響應,記錄debug日志
  5. dispatcherHandler指由Dispatcher擴充點指定的handler,預設為AllChannelHandler。用于控制請求的線程分發邏輯。請參見請求分發。
  6. dispatcherHandler會根據自身邏輯将請求分發到特定的線程
  7. 線程池或者IO線程會調用ChannelEventRunnable的run方法,根據狀态調用後續handler(DecodeHandler)的相應方法。
  8. DecodeHandler将接收到的Invocation或Response内的資料進行進一步解碼,解碼完成後調用後續的handler(HeaderExchangeHandler)處理。此處指代上圖中的7、8兩步。
  9. HeaderExchangeHandler會繼續調用進行後續的handler調用,但是如果此次調用需要傳回值,那麼由此處的received方法中将後續handler傳回值寫入到channel中傳回。
    • 後續的handler(DubboProtocol.requestHandler)調用的是reply方法,并不是received方法。
  10. DubboProtocol.requestHandler匿名内部類中調用後續Invoker,實作Provider的調用
  11. ProtocolFilterWrapper将Filter擴充點的實作類中@Activate注解的group值為consumer的Filter排序後組成過濾鍊,并逐個調用Filter,然後調用ListenerInvokerWrapper。
    • Filter鍊中的Filter根據@Activate注解的before、after、order屬性排序的
  12. 後續通過多層調用,調用到通過ProxyFactory擴充類動态生成的包裝類:WrapperN。
    • WrapperN中的N代表本次啟動建立的第N個包裝類,從0開始的AtomicLong靜态變量
  13. 通過WrapperN中的invokeMethod方法以及方法傳入參數調用實際的服務類相應方法。

Consumer接收響應

Consumer接收響應的過程與Provider響應過程前9步類似,即到HeaderExchangeHandler的received方法之前都一樣。下面是整體概述:

  1. HeaderExchangeHandler中received方法中,由于解碼出的對象是Response,是以将其直接傳入DefaultFuture的received方法中。
  2. DefaultFuture.received()根據response的ID,獲得之前Consumer請求時第12步的DefaultFuture對象。
  3. 根據DefaultFuture内部的并發程式設計邏輯,傳回響應的response。
    • Consumer請求時的request線程會阻塞在DefaultFuture的get方法内的done.await上
    • response線程接收傳回後,擷取和request相同的鎖。這個是必須的,且由于get是await的,是以可以擷取到鎖。
    • response在擷取到鎖後,觸發done.signal喚醒阻塞在done上的線程并在finally中釋放鎖。
    • request線程被喚醒,傳回response給Consumer頂層調用。
    • 以上是一個經典的等待-通知結構,也可以使用JDK自身的await、notify實作。兩個線程都必須在獲得鎖後在可以wait或notify,且notify後需要釋放鎖,此邏輯也可以實作簡單的線程池。

總結

  1. Dubbo内部大量使用了裝飾器模式(invoker、handler)、外觀模式等設計模式
  2. Dubbo最經典的還是服務擴充方面的設計(個人看法)
  3. Consumer的異步調用和同步調用本質上都是異步的
  4. Consumer和Provider在接收RPC時前置處理完全一緻,隻是由于消息的類型進行不同的處理

繼續閱讀