Dubbo架構學習整理

来源:https://www.cnblogs.com/butterfly100/archive/2018/07/08/9260731.html
-Advertisement-
Play Games

一. Dubbo誕生背景 隨著互聯網的發展和網站規模的擴大,系統架構也從單點的垂直結構往分散式服務架構演進,如下圖所示: 單一應用架構:一個應用部署所有功能,此時簡化CRUD的ORM框架是關鍵 垂直應用架構:應用拆分為不相干的幾個應用,前後端分離,此時用於加速前端頁面開發的Web MVC框架是關鍵 ...


一. Dubbo誕生背景

隨著互聯網的發展和網站規模的擴大,系統架構也從單點的垂直結構往分散式服務架構演進,如下圖所示:

  • 單一應用架構:一個應用部署所有功能,此時簡化CRUD的ORM框架是關鍵
  • 垂直應用架構:應用拆分為不相干的幾個應用,前後端分離,此時用於加速前端頁面開發的Web MVC框架是關鍵
  • 分散式服務架構:抽取各垂直應用的核心業務作為獨立服務,形成穩定的服務中心,此時用於提高業務復用及整合的分散式服務框架(RPC)是關鍵
  • 流動計算架構:當服務越來越多,容量的評估、小服務資源的浪費等問題逐漸顯現,此時用於提高機器利用率的實時資源調度和治理中心(SOA)是關鍵

 

當服務比較少時,可以通過 RMI 或 Hession 等工具,簡單的暴露和引用遠程服務,通過配置服務的URL地址來調用,通過F5等硬體負載均衡

當服務越來越多時,服務配置URL變的困難,F5硬體負載均衡的單點壓力越來越大。此時需要服務註冊中心,動態的註冊和發現服務,使服務的位置透明。服務調用實現軟負載均衡和Failover,降低對F5硬體負載均衡器的依賴

當服務間關係越來越複雜時,此時需要自動畫出服務間的依賴關係圖,來幫助架構師理清服務關係

當服務調用量越來越大時,服務需要多少台機器支撐,服務容量的問題就暴露出來了,此時需要統計服務每天的調用量、響應時間等性能指標作為容量規劃的參考。其次,還可以動態調整權重,將某台機器權重一直加大,直到響應時間到閥值,按照此時的訪問量反推服務的總容量

以上是Dubbo的基本需求,如下圖所示:

二. 整體架構

Dubbo的整體架構設計如圖所示:

 

Dubbo框架一共分10層,各層單向依賴。最上面的 Service 和 Config 為API,其他均為 SPI。左邊淡藍色的為 consumer 使用的介面,右邊淡綠色的為 provider 使用的介面,中間的為雙方都用到的介面。

黑色箭頭代表層之間的依賴關係;藍色虛線為初始化過程,即啟動時組裝鏈;紅色實線為方法調用過程;紫線為繼承關係。線上的文字為調用的方法。

1、介面服務層(Service):該層與業務邏輯相關,根據 provider 和 consumer 的業務設計對應的介面和實現

2、配置層(Config):對外配置介面,以 ServiceConfig 和 ReferenceConfig 為中心

3、服務代理層(Proxy):服務介面透明代理,生成服務的客戶端 Stub 和 服務端的 Skeleton,以 ServiceProxy 為中心,擴展介面為 ProxyFactory

4、服務註冊層(Registry):封裝服務地址的註冊和發現,以服務 URL 為中心,擴展介面為 RegistryFactory、Registry、RegistryService

5、路由層(Cluster):封裝多個提供者的路由和負載均衡,並橋接註冊中心,以Invoker 為中心,擴展介面為 Cluster、Directory、Router和LoadBlancce

6、監控層(Monitor):RPC調用次數和調用時間監控,以 Statistics 為中心,擴展介面為 MonitorFactory、Monitor和MonitorService

7、遠程調用層(Protocal):封裝 RPC 調用,以 Invocation 和 Result 為中心,擴展介面為 Protocal、Invoker和Exporter

8、信息交換層(Exchange):封裝請求響應模式,同步轉非同步。以 Request 和 Response 為中心,擴展介面為 Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer

9、網路傳輸層(Transport):抽象 mina 和 netty 為統一介面,以 Message 為中心,擴展介面為Channel、Transporter、Client、Server和Codec

10、數據序列化層(Serialize):可復用的一些工具,擴展介面為Serialization、 ObjectInput、ObjectOutput和ThreadPool 

各層關係說明:

  • Portocol 是核心層,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 調用,然後在 Invoker 的主過程上 Filter 攔截點
  • Cluster 是外圍概念,目的是將多個 Invoker 偽裝為一個 Invoker,這樣其它人只要關註 Protocol 層 Invoker 即可。只有一個 provider 時,是不需要 Cluster 的
  • Proxy 層封裝了所有介面的透明化代理,而在其它層都以 Invoker 為中心,只有到了暴露給用戶使用時,才用 Proxy 將 Invoker 轉成介面,或將介面實現轉成 Invoker,看起來像調本地服務一樣調遠程服務
  • Remoting 內部再劃為 Transport 傳輸層和 Exchange 信息交換層:Transport 層只負責單向消息傳輸,是對 Mina, Netty, Grizzly 的抽象;而 Exchange 層是在傳輸層之上封裝了 Request-Response 語義

Dubbo核心領域模型:

  • Protocol 是服務域,它是 Invoker 暴露和引用的主功能入口,它負責 Invoker 的生命周期管理
  • Invoker 是實體域,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉換成它。它代表一個可執行體,可向它發起 invoke 調用,它有可能是一個本地的實現,也可能是一個遠程的實現,也可能一個集群實現
  • Invocation 是會話域,它持有調用過程中的變數,比如方法名,參數等

Dubbo主要包括以下幾個節點:

  • Provider:暴露服務的服務提供方
  • Consumer:調用遠程服務的服務消費方
  • Registry:服務註冊和發現的註冊中心
  • Monitor:統計服務的調用次數和調用時間的監控中心
  • Container:服務運行容器

Consumer, Provider, Registry, Monitor代表邏輯部署節點。圖中只包含 RPC 層,不包含 Remoting層,Remoting整體隱藏在 Protocol 中。

藍色方框代表業務有交互,綠色方框代表只對Dubbo內部交互。藍色虛線為初始化時調用,紅色虛線為運行時非同步調用,紅色實線為運行時同步調用

0、服務在容器中啟動,載入,運行Provider

1、Provider在啟動時,向Registry註冊自己提供的服務

2、Consumer在啟動時,想Registry訂閱自己所需的服務

3、Registry給Consumer返回Provider的地址列表,如果Provider地址有變更(上線/下線機器),Registry將基於長連接推動變更數據給Consumer

4、Consumer從Provider地址列表中,基於軟負載均衡演算法,選一臺進行調用,如果失敗,重試另一臺調用

5、Consumer和Provider,在記憶體中累計調用次數和時間,定時每分鐘一次將統計數據發送到Monitor

將上面的服務調用流程展開,如下圖所示:

藍色虛線為初始化過程,即啟動時組裝鏈;紅色實線為方法調用過程,即運行時調用鏈;紫色實線為繼承

 

三、實現細節

Invoker 是 Dubbo 領域模型中非常重要的一個概念,很多設計思路都是向它靠攏,這就使得 Invoker 滲透在整個實現代碼里。下麵用一個精簡的圖來說明最重要的兩種 Invoker:服務提供 Invoker 和服務消費 Invoker:

① 定義服務介面:

public interface DemoService {
    String sayHello(String name);
}

② 服務提供者代碼: 

public class DemoServiceImpl implements DemoService {
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

ServiceConfig 類拿到對外提供服務的實際類 ref(如:DemoServiceImpl)通過 ProxyFactory.getInvoker 方法使用 ref 生成一個 AbstractProxyInvoker 實例,然後 通過 Protocol.export 方法新生成一個 Exporter 實例

當網路通訊層收到一個請求後,會找到對應的 Exporter 實例,並調用它所對應的 AbstractProxyInvoker 實例,從而真正調用了服務提供者的代碼

③ 服務消費者代碼:

public class DemoClientAction {
   
  private DemoService demoService;

  public void setDemoService(DemoService demoService) {
    this.demoService = demoService;
  }

  public void start() {
    String hello = demoService.sayHello("world");
  } }

首先通過 ReferenceConfig.init 方法調用 Protocal.refer 方法生成 Invoker 實例,接下來通過 ProxyFactory.getProxy 方法將 Invoker 轉換為客戶端需要的介面(如:DemoService)

DemoService 就是 consumer 端的 proxy,用戶代碼通過這個 proxy 調用其對應的 Invoker,通過 Invoker 實現真正的遠程調用

 

四. 功能特性

1. 配置

Dubbo可以採用全Spring的配置方式,基於Spring的Schema擴展進行載入,接入對業務透明,無API侵入。配置項可參考:schema 配置參考手冊

除了Spring配置,也可以使用API配置、屬性配置和註解配置方式。

配置之間的關係,如下圖所示:

provider side:

  • <dubbo:protocol/>:協議配置。用於配置提供服務的協議信息,協議由provider指定,consumer被動接受
  • <dubbo:service/>: 服務配置。暴露一個service,定義service的元信息,一個service可以用多個協議暴露,也可以註冊到多個註冊中心
  • <dubbo:provider/>:提供方配置【可選】。當 ProtocolConfig 和 ServiceConfig 某屬性沒有配置時,採用此預設值

consumer side:

  • <dubbo:reference/>:引用配置。用於創建一個遠程服務代理,一個引用可以指向多個註冊中心
  • <dubbo:consumer/>:消費方配置【可選】。當 ReferenceConfig 某屬性沒有配置時,採用此預設值

application shared:

  • <dubbo:application/>:應用配置。配置應用信息,包括provider和consumer
  • <dubbo:registry/>:註冊中心配置。配置連接註冊中心相關信息
  • <dubbo:monitor/>:監控中心配置【可選】。配置連接監控中心相關信息

sub-config:

  • <dubbo:method/>:方法配置。用於 ServiceConfig 和 ReferenceConfig 指定方法級的配置信息
  • <dubbo:argument/>:參數配置。用於指定方法參數配置

 

2. 集群容錯

服務調用時的過程如下圖:

 

Invoker:是Provider的一個可調用Service的抽象,封裝了Provider地址和Service介面信息

Directory:代表多個Invoker,可將它看為List<Invoker>,它的值是動態變化的,比如註冊中心推送變更

Cluster:將Directory的多個Invoker偽裝為一個Invoker,對上層透明。偽裝過程中包括容錯邏輯,例如:一個Invoker調用失敗後重試另一個Invoker

Router:從多個Invoker中按路由規則選出子集,例如:讀寫分離、應用隔離等

LoadBlance:從多個Invoker中選出具體的一個Invoker用於本次調用,選的過程包括負載均衡演算法,調用失敗後需要重選

當Cluster集群調用失敗時,Dubbo提供了多種容錯方案:

  • Failover【預設】:失敗時自動切換,重試其它伺服器。通常用於讀操作,可通過 retries="2" 來設置重試次數(不含第一次)
  • Failfast:快速失敗,只調用一次,失敗立即報錯。通常用於非冪等的寫操作,比如:新增記錄
  • Failsafe:失敗安全,失敗時直接忽略。通常用於寫入審計日誌等操作
  • Failback:失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知等操作
  • Forking:並行調用多個伺服器,只要一個成功即返回。通常用於實時性較高的讀操作,但浪費更多服務資源。可通過 forks="2" 設置最大並行數
  • Broadcast:廣播調用者,逐個調用,任意一臺報錯則報錯。通常用於通知所有提供者更新本地資源信息,如緩存、日誌等

 

3. 路由規則

路由規則決定一次dubbo服務調用的目標伺服器,分為腳本路由規則和條件路由規則,支持可擴展。向註冊中心寫入路由規則的操作通常由治理中心的頁面完成

  • 腳本路由規則:支持JDK腳本引擎的所有腳本,例如:javascript, groovy 等
  • 條件路由規則:基於條件的路由規則,例如:host = 10.20.153.10 => hsot = 10.20.153.11。=>之前是consumer匹配條件,所有參數和consumer的URL進行對比,如果consumer滿足匹配條件,則對consumer執行後面的過濾規則。=>之後是provider地址列表的過濾條件,所有參數和provider的URL進行對比,consumer只拿到過濾後的地址列表

 

4. 負載均衡

如上圖 LoadBlance 模塊所示:在集群負載均衡時,Dubbo提供了不同的策略:

  • Random【預設】:隨機,按權重設置隨機概率。調用量越大越均勻,有利於動態調整權重
  • RoundRobin:輪詢,按公約後的許可權設置輪詢比率。如果有台機器很慢,但沒掛,當請求到那一臺時就卡在那兒,久而久之,所有請求都卡在那台機器上
  • LeastActive:最少活躍調用數,活躍數指調用前後計數差,越慢的provider的調用前後計數差越大,使得慢的provider收到更少請求
  • ConsistentHash:一致性Hash,相同參數的請求發往同一臺provider,當一臺provider掛掉時,原本發往該機器的請求,基於虛擬節點會平攤到其他機器,不會引起劇烈變動

 

5. 線程派發模型 

如果事件處理的邏輯能迅速完成,並且不發生新的IO請求(例如在記憶體中記個標識),則在IO線程上處理更快,因為減少了線程池調度

如果事件處理的邏輯較慢,或需要發起新的IO請求(例如需要查詢資料庫),則必須派發到線程池,否則 IO 線程阻塞,將導致不能接受其他請求

因此需要不同的派發策略和不同的線程池組合來應對不同的場景:

Dispatcher:

  • all:所有消息派發到 ThreadPool,包括請求、響應、連接事件、斷開事件、心跳等
  • direct:所有消息不派發 ThreadPool,全在 IO 線程上執行
  • message:只有請求響應消息派發到 ThreadPool,其他連接事件、斷開事件、心跳等,在 IO 線程上執行
  • execution:只請求消息派發到 ThreadPool,其他事件包括響應事件、連接斷開事件、心跳等消息,在 IO 線程上執行
  • connection:在 IO 線程上,將連接斷開事件放入隊列,有序逐個執行,其他時間派發到 ThreadPool

ThreadPool:

  • fixed【預設】:固定大小線程池,啟動時建立線程,一直持有不關閉
  • cached:緩存線程池,空閑一分鐘自動刪除,需要時重建
  • limited:可伸縮線程池,線程數只增長不收縮,目的是為了避免收縮時大流量引起的性能問題
  • eager:優先創建Worker線程池,corePoolSize < 任務數量 < maximumPoolSize時,優先創建 Worker 處理任務。任務數量 > maximumPoolSize時,任務放入阻塞隊列中,阻塞隊列充滿時拋出 RejectExecutionException

 

6. 上下文信息和隱式參數

上下文中存放著當前調用過程中所需的環境信息。RpcContext 是一個 ThreadLocal 的臨時狀態記錄器,當接收或發起 RPC 請求時,RpcContext 都會發生變化。比如:A調用B,B調用C,在B調C之前,B機器上 RpcContext 記錄的是A調用B的信息。

通過 RpcContext 的 setAttachment 和 getAttachment 可以在 provider 和 consumer 之間進行參數的隱式傳遞

 

7. 非同步調用

基於NIO的非阻塞實現並行調用,客戶端不需要啟動多線程即可完成多個遠程服務的並行調用,相對比多線程開銷較小

 

8. 註冊中心

對於 provider,它需要發佈服務,而且由於應用系統的複雜性,服務的數量、類型也不斷膨脹;對於 consumer,它最關心如何獲取到它所需要的服務,而面對複雜的應用系統,需要管理大量的服務調用

服務註冊中心通過特性協議將服務統一管理起來,有效的優化內部應用對服務發佈/使用的流程。Dubbo提供的註冊中心有如下幾種類型可供選擇:

① ZooKeeper註冊中心

ZK是一個樹形的服務目錄,支持變更推送,適合作為Dubbo服務的註冊中心。流程如下:

  • provider啟動時,向 /dubbo/com.foo.BarService/providers 目錄下寫入自己的 URL 地址
  • consumer啟動時,訂閱 /dubbo/com.foo.BarService/providers 目錄下的 providers 地址,並向 /dubbo/com.foo.BarService/consumers 目錄下寫入自己的 URL 地址
  • 監控中心啟動時,訂閱 /dubbo/com.foo.BarService 目錄下的所有 provider 和 consumer URL地址

當 provider 出現斷電等異常停機時,註冊中心能自動刪除 provider 信息。當註冊中心重啟、或會話過期時,能自動恢復註冊數據和訂閱請求

② Multicase註冊中心

Multicast註冊中心不需要啟動任何中心節點,只要廣播地址即可互相發現

  • provider 啟動時廣播自己的地址
  • consumer 啟動時廣播訂閱請求
  • provider 收到訂閱請求時,單播自己的地址給訂閱者,若設置了 unicast=false,則廣播給訂閱者
  • consumer 收到 provider 地址時,連接地址進行 RPC 調用

組播受網路結構限制,只適合小規模應用或開發階段

③ Redis註冊中心

使用 redis 的 Key/Map 結構存儲數據結構:

  • 主 Key 為服務名和類型
  • Map 中的 Key 為 URL 地址
  • Map 中的 Value 為過期時間,用於判斷臟數據,臟數據由監控中心刪除

調用過程:

  1. provider 啟動時,向 Key:/dubbo/com.foo.BarService/providers 下,添加當前 provider 的地址
  2. 並向 Channel:/dubbo/com.foo.BarService/providers 發送 register 事件
  3. consumer 啟動時,向 Key:/dubbo/com.foo.BarService/providers 下,添加當前 consumer 的地址
  4. 並從 Channel:/dubbo/com.foo.BarService/providers 訂閱 register 和 unregister 事件
  5. consumer 收到 register 和 unregister 事件後,從 Key:/dubbo/com.foo.BarService/providers 下獲取 provider 地址列表
  6. 服務監控中心啟動時,從 Channel:/dubbo/* 訂閱 register 和 unregister,以及 subscribe 和 unsubscribe 事件
  7. 監控中心收到 register 和 unregister 事件後,從 Key:/dubbo/com.foo.BarService/providers 下獲取 provider 地址列表
  8. 監控中心收到 subscribe 和 unsubscribe 事件後,從 Key:/dubbo/com.foo.BarService/comsumers 下獲取 consumer 地址列表

 

參考

Dubbo開發文檔


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作為程式員(更高大尚的稱謂:研軟體研發)的我們,無論是用Javascript,還是.net, java語言,肯定都遇到過記憶體泄漏的問題。只不過他們都有GC機制來幫助程式員完成記憶體回收的事情,如果你是C++開發者(你懂的)。。。。。,如果你是前端開發者,肯定在使用Javascript(你或者會說,Js ...
  • 一、記憶體泄漏 由於某些原因不再需要的記憶體沒有被操作系統或則空閑記憶體池回收。編程語言中有多種管理記憶體的方式。這些方式從不同程度上會減少記憶體泄漏的幾率,高級語言嵌入了一個名為垃圾收集器的軟體,其工作是跟蹤記憶體分配和使用,以便找到不再需要分配記憶體的時間,在這種情況下,它將自動釋放它。然而,該過程是近似的, ...
  • https://blog.csdn.net/qq_24472595/article/details/70053863 ...
  • 為啥要說 promise ? 因為這是前端必須要掌握的一個知識,吹逼必備 同學們,開始上課了! 首先說說 Promise 是什麼? Promise 是JavaScript的第一個非同步標準模型,一個包含傳遞信息與狀態的對象,emmm...它的英語意思是承諾. so 它擁有的特點也有承諾的意思(兄弟們, ...
  • 業務中碰到微信小程式需要生成海報進行朋友圈分享,這個是非常常見的功能,沒想到實際操作的時候花了整整一天一夜才搞好,微信的 canvas 繪圖實在是太難用了,官方快點優化一下吧。 業務非常簡單,只需要將用到的圖片,文案素材拼裝到一張圖片,保存到本地就可以了。 首先創建畫布,將一張網上的圖片畫到畫布上。 ...
  • 參考學習:https://www.cnblogs.com/liwenzhou/p/7999532.html ...
  • 在 Android 和 Window 平臺下,有時候我們需要監聽返回按鍵的事件來進行相應的操作,也就是自定義返回按鍵事件。下麵根據一個例子來講解如何在 ionic 中自定義返回按鍵事件。 功能需求 首先看下要通過自定義返回按鍵事件來實現的需求(均在點擊返回鍵操作之後): 如果鍵盤打開,則隱藏鍵盤; ...
  • 打開包的 -- 吧xsd文件改為: 其他的也類似 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...