Dubbo原理之深入理解

1 Dubbo原理

1.1 Dubbo架構圖

1.1.1 Dubbo基本理解

Dubbo:一個分佈式、高性能、透明化的RPC服務框架
作用:提供服務自動註冊、自動發現等高效服務治理方案
在這裏插入圖片描述

1.1.2 架構內角色說明:

  • Provider:提供者,服務發佈方.
  • Consumer:消費者, 調用服務方
  • ContainerDubbo容器.依賴於Spring容器.
  • Registry: 註冊中心.當Container啓動時把所有可以提供的服務列表上Registry中進行註冊,作用:告訴Consumer提供了什麼服務和服務方在哪裏
  • Monitor:監聽器
  • 虛線都是異步訪問,實線都是同步訪問
  • 藍色虛線:在啓動時完成的功能
  • 紅色虛線(實線)都是程序運行過程中執行的功能
  • 所有的角色都是可以在單獨的服務器上,所以必須遵守特定的協議
  • 上圖中,除了監視器monitor這裏是短連接,其他連接都是長連接

1.1.3 運行原理

  • 啓動容器,相當於在啓動DubboProvider
  • 啓動後會去註冊中心進行註冊,註冊所有可以提供的服務列表
  • Consumer啓動後會去Registry中獲取服務列表和Provider的地址,進行訂閱.
  • Provider有修改後,註冊中心會把消息推送給Consummer,使用了觀察者設計模式(又叫發佈/訂閱設計模式)
  • 根據獲取到的Provider地址,真實調用Provider中功能,在Consumer方使用了代理設計模式,創建一個Provider方類的一個代理對象,通過代理對象獲取Provider中真實功能,起到保護Provider真實功能的作用.
  • ConsumerProvider每隔1分鐘向Monitor發送統計信息,統計信息包含,訪問次數,頻率等

1.2 底層調用真正原理

Dubbo底層採用Socket進行通信,Dubbo缺省協議採用單一長連接和NIO異步通訊,適合於小數據量大併發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況

  • 長連接:一組信息(通常是字節)的各位數據被同時傳送的通信方法稱爲並行通信。並行通信依靠並行I/O接口實現。並行通信速度快,但傳輸線根數多,只適用於近距離(相距數公尺)的通信。
  • 短連接 :一組信息的各位數據被逐位順序傳送的通信方式稱爲串行通信。串行通信可通過串行接口來實現。串行通信速度慢,但傳輸線少,適宜長距離通信。

1.2.1 底層調用原理

分析源代碼,基本原理如下:

  • client一個線程調用遠程接口,生成一個唯一的ID(比如一段隨機字符串,UUID等),Dubbo是使用AtomicLong0開始累計數字的
  • 將打包的方法調用信息(如調用的接口名稱,方法名稱,參數值列表等),和處理結果的回調對象callback,全部封裝在一起,組成一個對象object
  • 向專門存放調用信息的全局ConcurrentHashMap裏面put(ID, object)
  • ID和打包的方法調用信息封裝成一對象connRequest,使用IoSession.write(connRequest)異步發送出去
  • 當前線程再使用callback的get()方法試圖獲取遠程返回的結果,在get()內部,則使用synchronized獲取回調對象callback的鎖, 再先檢測是否已經獲取到結果,如果沒有,然後調用callback的wait()方法,釋放callback上的鎖,讓當前線程處於等待狀態
  • 服務端接收到請求並處理後,將結果(此結果中包含了前面的ID,即回傳)發送給客戶端,客戶端socket連接上專門監聽消息的線程收到消息,分析結果,取到ID,再從前面的ConcurrentHashMap裏面get(ID),從而找到callback,將方法調用結果設置到callback對象裏。
  • 監聽線程接着使用synchronized獲取回調對象callback的鎖(因爲前面調用過wait(),那個線程已釋放callback的鎖了),再notifyAll(),喚醒前面處於等待狀態的線程繼續執行(callbackget()方法繼續執行就能拿到調用結果了),至此,整個過程結束

1.2.2 調用相關問題

當前線程怎麼讓它暫停,等結果回來後,再向後執行?
答:先生成一個對象obj,在一個全局mapput(ID,obj)存放起來,再用synchronized獲取obj鎖,再調用obj.wait()讓當前線程處於等待狀態,然後另一消息監聽線程等到服 務端結果來了後,再map.get(ID)找到obj,再用synchronized獲取obj鎖,再調用obj.notifyAll()喚醒前面處於等待狀態的線程。

正如前面所說,Socket通信是一個全雙工的方式,如果有多個線程同時進行遠程方法調用,這時建立在client server之間的socket連接上會有很多雙方發送的消息傳遞,前後順序也可能是亂七八糟的,server處理完結果後,將結果消息發送給clientclient收到很多消息,怎麼知道哪個消息結果是原先哪個線程調用的?
答:使用一個ID,讓其唯一,然後傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪個線程的了。

1.2.3 調用協議說明

在這裏插入圖片描述
Dubbo缺省協議,使用基於mina1.1.7+hessian3.2.1tbremoting交互。

  • 連接個數:單連接
  • 連接方式:長連接
  • 傳輸協議:TCP
  • 傳輸方式:NIO異步傳輸
  • 序列化:Hessian二進制序列化
  • 適用範圍:傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,儘量不要用dubbo協議傳輸大文件或超大字符串。
  • 適用場景:常規遠程服務方法調用
    通常,一個典型的同步遠程調用應該是這樣的:

1.2.4 provider和consumer的invoke

下面我們用一個精簡的圖來說明最重要的兩種Invoker:服務提供Invoker和服務消費Invoker
在這裏插入圖片描述

1.2.5 服務暴露和消費的詳細過程

1.2.5.1 provider暴露服務的詳細過程

服務提供者暴露服務的主過程:
在這裏插入圖片描述
首先ServiceConfig類拿到對外提供服務的實際類ref(如:HelloWorldImpl),然後通過ProxyFactory類的getInvoker方法使用ref生成一個AbstractProxyInvoker實例,到這一步就完成具體服務到Invoker的轉化。

接下來就是Invoker轉換到Exporter的過程。
Dubbo處理服務暴露的關鍵就在Invoker轉換到Exporter的過程(如上圖中的紅色部分),下面我們以DubboRMI這兩種典型協議的實現來進行說明:

  • Dubbo的實現:
    Dubbo協議的Invoker轉爲Exporter發生在DubboProtocol類的export方法,它主要是打開socket偵聽服務,並接收客戶端發來的各種請求,通訊細節由Dubbo自己實現。
  • RMI的實現:
    RMI,(Remote Method Invoke 遠程方法調用,只支持java)協議的Invoker轉爲Exporter發生在RmiProtocol類的export方法,
    它通過SpringDubboJDK來實現RMI服務,通訊細節這一塊由JDK底層來實現,這就省了不少工作量。

1.2.5.2 consumer消費服務過程

服務消費的主過程:
在這裏插入圖片描述
首先ReferenceConfig類的init方法調用Protocolrefer方法生成Invoker實例(如上圖中的紅色部分),這是服務消費的關鍵。
接下來,然後通過ProxyFactory類的getProxy方法把Invoker轉換爲客戶端需要的接口(如:HelloWorld)

2 Dubbo分層

Dubbo 大致上分爲三層,分別是:

  • 業務層
  • RPC
  • Remoting
    在這裏插入圖片描述
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章