Dubbo架構理解
架構整體設計
Dubbo呼叫關系說明

在這里主要由四部分組成:
- Provider: 暴露服務的服務提供方
Protocol 負責提供者和消費者之間協議互動資料
Service 真實的業務服務資訊 可以理解成介面 和 實作
Container Dubbo的運行環境
- Consumer: 呼叫遠程服務的服務消費方
Protocol 負責提供者和消費者之間協議互動資料
Cluster 感知提供者端的串列資訊
Proxy 可以理解成 提供者的服務呼叫代理類,由它接管 Consumer 中的介面呼叫邏輯
- Registry: 注冊中心,用于作為服務發現和路由配置等作業,提供者和消費者都會在這里進行注冊
- Monitor: 用于提供者和消費者中的資料統計,比如呼叫頻次,成功失敗次數等資訊,
啟動和執行流程說明:
- 提供者端啟動 容器負責把Service資訊加載 并通過Protocol 注冊到注冊中心
- 消費者端啟動 通過監聽提供者串列來感知提供者資訊 并在提供者發生改變時 通過注冊中心及時,通知消費端
- 消費方發起請求通過Proxy模塊
- 利用Cluster模塊 來選擇真實的要發送給的提供者資訊
- 交由Consumer中的Protocol 把資訊發送給提供者
- 提供者同樣需要通過 Protocol 模塊來處理消費者的資訊
- 最后由真正的服務提供者 Service 來進行處理
整體的呼叫鏈路

業務邏輯層 RPC層(遠程程序呼叫) Remoting (遠程資料傳輸)
整體鏈路呼叫的流程:
- 消費者通過Interface進行方法呼叫 統一交由消費者端的 Proxy 通過ProxyFactory 來進行代理物件的創建 使用到了 jdk javassist技術
2.交給Filter 這個模塊 做一個統一的過濾請求 在SPI案例中涉及過
3.接下來會進入最主要的Invoker呼叫邏輯
通過Directory 去配置中心讀取資訊 最終通過list方法獲取所有的Invoker
通過Cluster模塊 根據選擇的具體路由規則 來選取Invoker串列
通過LoadBalance模塊 根據負載均衡策略 選擇一個具體的Invoker 來處理請求
如果執行中出現錯誤 并且Consumer階段配置了重試機制 則會重新嘗試執行 - 繼續經過Filter 進行執行功能的前后封裝 Invoker 選擇具體的執行協議
- 客戶端 進行編碼和序列化 然后發送資料
- 到達Provider中的 Server 在這里進行 反編碼 和 反序列化的接收資料
- 使用Exporter選擇執行器
- 交給Filter 進行一個提供者端的過濾 到達 Invoker 執行器
- 通過Invoker 呼叫介面的具體實作 然后回傳
Dubbo原始碼整體設計

右邊的黑色箭頭代表層之間的依賴關系,每一層都可以剝離上層被復用,其中,Service 和 Config 層為 API,其它各層均為 SPI,
分層介紹:
Business 業務邏輯層
- service 業務層 包括業務代碼 比如 介面 實作類 直接面向開發者
RPC層 遠程程序呼叫層
- config 配置層 對外提供配置 以ServiceConfig ReferenceConfig 為核心 可以直接初始化配置類 也可以決議組態檔生成
- proxy 服務代理層 無論是生產者 還是消費者 框架都會產生一個代理類 整個程序對上層透明 就是業務層對遠程呼叫無感
- registry 注冊中心層 封裝服務地址的注冊與發現 以服務的URL為中心
- cluster 路由層 (集群容錯層) 提供了多個提供者的路由和負載均衡 并且它橋接注冊中心 以Invoker為核心
- monitor 監控層 RPC呼叫相關的資訊 如 呼叫次數 成功失敗的情況 呼叫時間等 在這一層完成
- protocol 遠程呼叫層 封裝RPC呼叫 無論是服務的暴露 還是 服務的參考 都是在Protocol中作為主功能入口 負責Invoker的整個生命周期 Dubbo中所有的模型都向Invoker靠攏
Remoting層 遠程資料傳輸層
- exchange 資訊交換層 封裝請求和回應的模式 如把請求由同步 轉換成異步
- transport 網路傳輸層 統一網路傳輸的介面 比如 netty 和 mina 統一為一個網路傳輸介面
- serialize 資料序列化層 負責管理整個框架中的資料傳輸的序列化 和反序列化
服務注冊與消費原始碼剖析

注冊中心Zookeeper目錄結構
例如:只有一個提供者和消費者, com.lagou.service.HelloService 為我們所提供的服務,

Zookeeper的目錄結構如下:

- consumers: 當前服務下面所有的消費者串列(URL)
- providers: 當前服務下面所有的提供者串列(URL)
- configuration: 當前服務下面的配置資訊資訊,provider或者consumer會通過讀取這里的配置資訊來獲取配置
- routers: 當消費者在進行獲取提供者的時,會通過這里配置好的路由來進行適配匹配規則,

- 提供者會在 providers 目錄下進行自身的進行注冊,
- 消費者會在 consumers 目錄下進行自身注冊,并且監聽 provider 目錄,以此通過監聽提供者增加或者減少,實作服務發現,
- Monitor模塊會對整個服務級別做監聽,用來得知整體的服務情況,以此就能更多的對整體情況做監控,
服務的注冊程序分析
服務注冊(暴露)程序

首先 ServiceConfig 類拿到對外提供服務的實際類 ref(具體的介面實作類),然后通過 ProxyFactory 介面實作類中的 getInvoker 方法使用 ref 生成一個 AbstractProxyInvoker 實體,到這一步就完成具體服務到 Invoker 的轉化,接下來就是 Invoker 轉換到 Exporter 的程序,
查看 ServiceConfifig 類,重點查看 ProxyFactory 和 Protocol 型別的屬性 以及 ref
服務注冊暴露時序圖:

Registry中的類目錄結構

- 每個層級代表繼承自父級
- 這里面 RegistryService 就是對外提供注冊機制的介面,
- 其下面 Registry 也同樣是一個介面,是對 RegistryService 的集成,并且繼承了 Node 介面,說明注冊中心也是基于URL去做的,
- AbstractRegistry 是對注冊中心的封裝,其主要會對本地注冊地址的封裝,主要功能在于遠程注冊中心不可用的時候,可以采用本地的注冊中心來使用,
- FailbackRegistry 從名字中可以看出來,失敗自動恢復,后臺記錄失敗請求,定時重發功能,
- 最深的一層則更多是真實的第三方渠道實作,
URL規則詳解 和 服務本地快取
URL規則詳解

服務本地快取
dubbo呼叫者需要通過注冊中心(例如:ZK)注冊資訊,獲取提供者,但是如果頻繁往從ZK獲取資訊,肯定會存在單點故障問題,所以dubbo提供了將提供者資訊快取在本地的方法,
Dubbo在訂閱注冊中心的回呼處理邏輯當中會保存服務提供者資訊到本地快取檔案當中(同步/異步兩種方式),以URL緯度進行全量保存,
Dubbo在服務參考程序中會創建registry物件并加載本地快取檔案,會優先訂閱注冊中心,訂閱注冊中心失敗后會訪問本地快取檔案內容獲取服務提供資訊,
構建流程:
- 初始化AbstractRegistry建構式,并且從系統中讀取已有的配置資訊,

- 方法notify(url.getBackupUrls()) ->notify(url, listener, filterEmpty(url, urls)) -> saveProperties(url),通過尋找,這個屬性的設定值只有在一個地方: saveProperties ,這里也有基于版本號的的更改

- doSaveProperties(long version),最終快取的保存方法,

Dubbo 消費程序分析

首先 ReferenceConfig 類的 init 方法呼叫 createProxy() ,期間 使用 Protocol 呼叫 refer 方法生 成 Invoker 實體(如上圖中的紅色部分),這是服務消費的關鍵,接下來使用ProxyFactory把 Invoker 轉換為客戶端需要的介面

Dubbo擴展SPI原始碼剖析
getExtensionLoader 加載程序

注:
- cachedAdaptiveClass: 當前Extension型別對應的AdaptiveExtension型別(只能一個)
- cachedWrapperClasses: 當前Extension型別對應的所有Wrapper實作型別(無順序)
- cachedActivates: 當前Extension實作自動激活實作快取(map,無序)
- cachedNames: 擴展點實作類對應的名稱(如配置多個名稱則值為第一個)
- **getExtension獲取擴展點的方法 **
- injectExtension注入其他擴展點的物體,用于擴展點和其他的擴展點相互打通(set的方式注入)
Adaptive功能實作原理
Adaptive的主要功能是對所有的擴展點進行封裝為一個類,通過URL傳入引數的時動態選擇需要使用的擴展點,其底層的實作原理就是動態代理,

createAdaptiveExtensionClass()方法生成具體的Adaptive代理物件,通過AdaptiveClassCodeGenerator下的generate()方法做將具體的介面實作類重新做字串拼接插入需要提前呼叫的方法和URL引數判斷,最后通過org.apache.dubbo.common.compiler.Compiler 將字串編譯成class檔案,
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler =
ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class)
.getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
**
集群容錯原始碼剖析
集群容錯的組件包含 Cluster、 Cluster Invoker、Directory、Router 和 LoadBalance 等,

集群作業程序可分為兩個階段:
- 第一個階段是在服務消費者初始化期間,集群 Cluster 實作類為服務消 費者創建 Cluster Invoker 實體,即上圖中的 merge 操作,
- 第二個階段是在服務消費者進行遠程呼叫時,以FailoverClusterInvoker為例:
- 該型別ClusterInvoker首先會呼叫Directory的list方法列舉Invoker串列(可將Invoker簡單理解為服務提供者),Directory的用途是保存Invoker串列,可簡單類比為List,其實作類RegistryDirectory是一個動態服務目錄,可感知注冊中心配置的變化,它所持有的Invoker串列會隨著注冊中心內容的變化而變化,
- 每次變化后,RegistryDirectory會動態增刪Invoker,并呼叫Router的route方法進行路由,過濾掉不符合路由規則的Invoker,
- 當FailoverClusterInvoker拿到Directory回傳的Invoker串列后,它會通過LoadBalance從Invoker串列中選擇一個Invoker,
- 最后FailoverClusterInvoker會將引數傳給LoadBalance選擇出的Invoker實體的invoker方法,進行真正的遠程呼叫,
Dubbo 主要提供了這樣幾種容錯方式:
- Failover Cluster - 失敗自動切換 失敗時會重試其它服務器
- Failfast Cluster - 快速失敗 請求失敗后快速回傳例外結果 不重試
- Failsafe Cluster - 失敗安全 出現例外 直接忽略 會對請求做負載均衡
- Failback Cluster - 失敗自動恢復 請求失敗后 會自動記錄請求到失敗佇列中
- Forking Cluster - 并行呼叫多個服務提供者 其中有一個回傳 則立即回傳結果
資訊快取介面Directory
Directory是Dubbo中的一個介面,主要用于快取當前可以被呼叫的提供者串列資訊,我們在消費者進 行呼叫時都會通過這個介面來獲取所有的提供者串列,再進行后續處理,
public interface Directory<T> extends Node {
//獲取服務型別
Class<T> getInterface();
//獲取本次呼叫可以使用的提供者資訊
List<Invoker<T>> list(Invocation invocation) throws RpcException;
//獲取所有提供者資訊
List<Invoker<T>> getAllInvokers();
URL getConsumerUrl();
}
RouterChain#route 方法,這里所做的就是依次遍歷所有的路由,然后分別執行并回傳,這 也就是整體的路由規則的實作,

路由規則實作原理
RouterChain 中的 Router下的子類 ConditionRouter 為例
這兩個屬性比較關鍵,這兩個屬性也是判斷的關鍵,

init方法做路由規則的初始化,也對上面的whenCondition與thenCondition做了初始化,其中paseRule方法是決議路由規則的方法并回傳Map其值MatchPair存盤了匹配條件和不匹配條件的Set集合

MatchPair

route方法則是篩選出符合條件的invoke并進行回傳(List)

Cluster組件
Cluster 主要用于代理真正的Invoker執行時做處理,提供了多種容錯方案,

這里以默認的FailoverCluster為例


通過 AbstractClusterInvoker#invoke 呼叫 FailoverClusterInvoker#doInvoke



負載均衡實作原理
Cluster 中經過負載選擇真正 Invoker 的代碼

以RandomLoadBalance為例,doSelect負責處理具體的負載均衡操作,

Invoker執行邏輯
Invoker是真實執行請求的組件,

以DubboInvoker呼叫為例

ExchangeClient為主要資料傳輸的客戶端,其繼承了HeaderExchangeChannel介面

doInvoke方法中根據isOneway判斷是否執行異步請求

ExchangeClient 介面下的 **request **方法為真實發送訊息的方法

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/257081.html
標籤:其他
