秒懂Dubbo介面(原理篇)

 

引言

在上文性能基礎之常見RPC框架淺析中我們詳細介紹常見的RPC框架,本文將詳細介紹Dubbo介面。

背景

隨著互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。
在這裡插入圖片描述

單一應用架構

當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的數據訪問框架(ORM)是關鍵。

垂直應用架構

當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。

分散式服務架構

當垂直應用越來越多,應用之間交互不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務復用及整合的分散式服務框架(RPC)是關鍵。

流動計算架構

當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個調度中心基於訪問壓力實時管理集群容量,提高集群利用率。此時,用於提高機器利用率的資源調度和治理中心(SOA)是關鍵。

為什麼要用 Dubbo?

Dubbo 的誕生和 SOA 分散式架構的流行有著莫大的關係。SOA 面向服務的架構(Service Oriented Architecture),也就是把工程按照業務邏輯拆分成服務層、表現層兩個工程。服務層中包含業務邏輯,只需要對外提供服務即可。表現層只需要處理和頁面的交互,業務邏輯都是調用服務層的服務來實現。SOA架構中有兩個主要角色:服務提供者(Provider)和服務使用者(Consumer)。
在這裡插入圖片描述
如果你要開發分散式程式,你也可以直接基於 HTTP 介面進行通訊,但是為什麼要用 Dubbo呢?

我覺得主要可以從 Dubbo 提供的下面四點特性來說為什麼要用 Dubbo:

  • 負載均衡——同一個服務部署在不同的機器時該調用那一台機器上的服務
  • 服務調用鏈路生成——隨著系統的發展,服務越來越多,服務間依賴關係變得錯蹤複雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關係。Dubbo 可以為我們解決服務之間互相是如何調用的。
  • 服務訪問壓力以及時長統計、資源調度和治理——基於訪問壓力實時管理集群容量,提高集群利用率。
  • 服務降級——某個服務掛掉之後調用備用服務
    另外,Dubbo 除了能夠應用在分散式系統中,也可以應用在現在比較火的微服務系統中。不過,由於 Spring Cloud 在微服務中應用更加廣泛,所以,我覺得一般我們提 Dubbo 的話,大部分是分散式系統的情況。

我們剛剛提到了分散式這個概念,下面再給大家介紹一下什麼是分散式?為什麼要分散式?

什麼是分散式?

分散式或者說 SOA 分散式重要的就是面向服務,說簡單的分散式就是我們把整個系統拆分成不同的服務然後將這些服務放在不同的伺服器上減輕單體服務的壓力提高並發量和性能。比如電商系統可以簡單地拆分成訂單系統、商品系統、登錄系統等等,拆分之後的每個服務可以部署在不同的機器上,如果某一個服務的訪問量比較大的話也可以將這個服務同時部署在多台機器上。

為什麼要分散式?

從開發角度來講單體應用的程式碼都集中在一起,而分散式系統的程式碼根據業務被拆分。所以,每個團隊可以負責一個服務的開發,這樣提升了開發效率。另外,程式碼根據業務拆分之後更加便於維護和擴展。

另外,我覺得將系統拆分成分散式之後不光便於系統擴展和維護,更能提高整個系統的性能。你想一想嘛?把整個系統拆分成不同的服務/系統,然後每個服務/系統 單獨部署在一台伺服器上,是不是很大程度上提高了系統性能呢?

Dubbo 的架構

Dubbo 的架構圖解

在這裡插入圖片描述
述節點簡單說明:

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

調用關係說明:

  1. 服務容器負責啟動,載入,運行服務提供者。
  2. 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
  3. 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
  4. 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連接推送變更數據給消費者。
  5. 服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
  6. 服務消費者和提供者,在記憶體中累計調用次數和調用時間,定時每分鐘8. 發送一次統計數據到監控中心。

重要知識點總結:

  • 註冊中心負責服務地址的註冊與查找,相當於目錄服務,服務提供者和消費者只在啟動時與註冊中心交互,註冊中心不轉發請求,壓力較小
  • 監控中心負責統計各服務調用次數,調用時間等,統計先在記憶體匯總後每分鐘一次發送到監控中心伺服器,並以報表展示
  • 註冊中心,服務提供者,服務消費者三者之間均為長連接,監控中心除外
  • 註冊中心通過長連接感知服務提供者的存在,服務提供者宕機,註冊中心將立即推送事件通知消費者
  • 註冊中心和監控中心全部宕機,不影響已運行的提供者和消費者,消費者在本地快取了提供者列表
  • 註冊中心和監控中心都是可選的,服務消費者可以直連服務提供者
  • 服務提供者無狀態,任意一台宕掉後,不影響使用
  • 服務提供者全部宕掉後,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

Dubbo 工作原理

在這裡插入圖片描述
圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關係,每一層都可以剝離上層被複用,其中,Service 和 Config 層為 API,其它各層均為 SPI。

各層說明:

  • 第一層:service層,介面層,給服務提供者和消費者來實現的
  • 第二層:config層,配置層,主要是對dubbo進行各種配置的
  • 第三層:proxy層,服務介面透明代理,生成服務的客戶端 Stub 和伺服器端 Skeleton
  • 第四層:registry層,服務註冊層,負責服務的註冊與發現
  • 第五層:cluster層,集群層,封裝多個服務提供者的路由以及負載均衡,將多個實例組合成一個服務
  • 第六層:monitor層,監控層,對rpc介面的調用次數和調用時間進行監控
  • 第七層:protocol層,遠程調用層,封裝rpc調用
  • 第八層:exchange層,資訊交換層,封裝請求響應模式,同步轉非同步
  • 第九層:transport層,網路傳輸層,抽象mina和netty為統一介面
  • 第十層:serialize層,數據序列化層。網路傳輸需要。

從上圖可以看出,Dubbo對於服務提供方和服務消費方,從框架的10層中分別提供了各自需要關心和擴展的介面,構建整個服務生態系統(服務提供方和服務消費方本身就是一個以服務為中心的)。

根據官方提供的,對於上述各層之間關係的描述,如下所示:

  • 在 RPC 中,Protocol 是核心層,也就是只要有Protocol + Invoker + Exporter 就可以完成非透明的RPC調用,然後在Invoker的主過程上Filter攔截點。

  • 圖中的 Consumer 和Provider是抽象概念,只是想讓看圖者更直觀的了解哪些類分屬於客戶端與伺服器端,不用Client和Server的原因是Dubbo在很多場景下都使用Provider、Consumer、Registry、Monitor劃分邏輯拓普節點,保持統一概念。

  • 而Cluster是外圍概念,所以Cluster的目的是將多個Invoker偽裝成一個Invoker,這樣其它人只要關注Protocol層Invoker即可,加上Cluster或者去掉Cluster對其它層都不會造成影響,因為只有一個提供者時,是不需要Cluster的。

  • Proxy層封裝了所有介面的透明化代理,而在其它層都以Invoker為中心,只有到了暴露給用戶使用時,才用Proxy將Invoker轉成介面,或將介面實現轉成Invoker,也就是去掉Proxy層RPC是可以Run的,只是不那麼透明,不那麼看起來像調本地服務一樣調遠程服務。

  • 而Remoting 實現是Dubbo協議的實現,如果你選擇RMI協議,整個Remoting都不會用上,Remoting內部再劃為Transport傳輸層和Exchange資訊交換層,Transport層只負責單向消息傳輸,是對Mina、Netty、Grizzly的抽象,它也可以擴展UDP傳輸,而Exchange層是在傳輸層之上封裝了Request-Response語義。

  • Registry和Monitor實際上不算一層,而是一個獨立的節點,只是為了全局概覽,用層的方式畫在一起。

從上面的架構圖中,我們可以了解到,Dubbo作為一個分散式服務框架,主要具有如下幾個核心的要點:

服務定義

服務是圍繞服務提供方和服務消費方的,服務提供方實現服務,而服務消費方調用服務。

服務註冊

對於服務提供方,它需要發布服務,而且由於應用系統的複雜性,服務的數量、類型也不斷膨脹;對於服務消費方,它最關心如何獲取到它所需要的服務,而面對複雜的應用系統,需要管理大量的服務調用。而且,對於服務提供方和服務消費方來說,他們還有可能兼具這兩種角色,即既需要提供服務,有需要消費服務。

通過將服務統一管理起來,可以有效地優化內部應用對服務發布/使用的流程和管理。服務註冊中心可以通過特定協議來完成服務對外的統一。

Dubbo提供的註冊中心有如下幾種類型可供選擇:

  • Multicast註冊中心

  • Zookeeper註冊中心

  • Redis註冊中心

  • Simple註冊中心

服務監控

無論是服務提供方,還是服務消費方,他們都需要對服務調用的實際狀態進行有效的監控,從而改進服務品質。

遠程通訊與資訊交換

遠程通訊需要指定通訊雙方所約定的協議,在保證通訊雙方理解協議語義的基礎上,還要保證高效、穩定的消息傳輸。Dubbo繼承了當前主流的網路通訊框架,主要包括如下幾個:

  • Mina

  • Netty

  • Grizzly

服務調用

下面從Dubbo官網直接拿來,看一下基於RPC層,服務提供方和服務消費方之間的調用關係,如圖所示:
在這裡插入圖片描述

上圖中,藍色的表示與業務有交互,綠色的表示只對Dubbo內部交互。上述圖所描述的調用流程如下:

  • 服務提供方發布服務到服務註冊中心;

  • 服務消費方從服務註冊中心訂閱服務;

  • 服務消費方調用已經註冊的可用服務

接著,將上面抽象的調用流程圖展開,詳細如圖所示:
在這裡插入圖片描述

註冊/註銷服務

服務的註冊與註銷,是對服務提供方角色而言,那麼註冊服務與註銷服務的時序圖,如圖所示:

在這裡插入圖片描述

服務訂閱/取消

為了滿足應用系統的需求,服務消費方的可能需要從服務註冊中心訂閱指定的有服務提供方發布的服務,在得到通知可以使用服務時,就可以直接調用服務。反過來,如果不需要某一個服務了,可以取消該服務。下面看一下對應的時序圖,如圖所示:

在這裡插入圖片描述

協議支援

Dubbo支援多種協議,如下所示:

  • Dubbo協議

  • Hessian協議

  • HTTP協議

  • RMI協議

  • WebService協議

  • Thrift協議

  • Memcached協議

  • Redis協議

在通訊過程中,不同的服務等級一般對應著不同的服務品質,那麼選擇合適的協議便是一件非常重要的事情。你可以根據你應用的創建來選擇。例如,使用RMI協議,一般會受到防火牆的限制,所以對於外部與內部進行通訊的場景,就不要使用RMI協議,而是基於HTTP協議或者Hessian協議。

參考補充

Dubbo以包結構來組織各個模組,各個模組及其關係,如圖所示:

在這裡插入圖片描述

可以通過Dubbo的程式碼(使用Maven管理)組織,與上面的模組進行比較。簡單說明各個包的情況:

  • dubbo-common 公共邏輯模組,包括Util類和通用模型。

  • dubbo-remoting 遠程通訊模組,相當於Dubbo協議的實現,如果RPC用RMI協議則不需要使用此包。

  • dubbo-rpc 遠程調用模組,抽象各種協議,以及動態代理,只包含一對一的調用,不關心集群的管理。

  • dubbo-cluster 集群模組,將多個服務提供方偽裝為一個提供方,包括:負載均衡、容錯、路由等,集群的地址列表可以是靜態配置的,也可以是由註冊中心下發。

  • dubbo-registry 註冊中心模組,基於註冊中心下發地址的集群方式,以及對各種註冊中心的抽象。

  • dubbo-monitor 監控模組,統計服務調用次數,調用時間的,調用鏈跟蹤的服務。

  • dubbo-config 配置模組,是Dubbo對外的API,用戶通過Config使用Dubbo,隱藏Dubbo所有細節。

  • dubbo-container 容器模組,是一個Standalone的容器,以簡單的Main載入Spring啟動,因為服務通常不需要Tomcat/JBoss等Web容器的特性,沒必要用Web容器去載入服務。

Dubbo 的負載均衡策略

先來解釋一下什麼是負載均衡

先來個官方的解釋。

維基百科對負載均衡的定義:負載均衡改善了跨多個計算資源(例如電腦,電腦集群,網路鏈接,中央處理單元或磁碟驅動的的工作負載分布。負載平衡旨在優化資源使用,最大化吞吐量,最小化響應時間,並避免任何單個資源的過載。使用具有負載平衡而不是單個組件的多個組件可以通過冗餘提高可靠性和可用性。負載平衡通常涉及專用軟體或硬體

上面講的大家可能不太好理解,再用通俗的話給大家說一下。

比如我們的系統中的某個服務的訪問量特別大,我們將這個服務部署在了多台伺服器上,當客戶端發起請求的時候,多台伺服器都可以處理這個請求。那麼,如何正確選擇處理該請求的伺服器就很關鍵。假如,你就要一台伺服器來處理該服務的請求,那該服務部署在多台伺服器的意義就不復存在了。負載均衡就是為了避免單個伺服器響應同一請求,容易造成伺服器宕機、崩潰等問題,我們從負載均衡的這四個字就能明顯感受到它的意義。

再來看看 Dubbo 提供的負載均衡策略

在集群負載均衡時,Dubbo 提供了多種均衡策略,默認為 random 隨機調用。可以自行擴展負載均衡策略,參見:負載均衡擴展

Random LoadBalance(默認,基於權重的隨機負載均衡機制)

  • 隨機,按權重設置隨機概率。
  • 在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。

基於權重的隨機負載均衡機制

RoundRobin LoadBalance(不推薦,基於權重的輪詢負載均衡機制)

  • 輪循,按公約後的權重設置輪循比率。
  • 存在慢的提供者累積請求的問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。

基於權重的輪詢負載均衡機制

LeastActive LoadBalance

  • 最少活躍調用數,相同活躍數的隨機,活躍數指調用前後計數差。
  • 使慢的提供者收到更少請求,因為越慢的提供者的調用前後計數差會越大。

ConsistentHash LoadBalance

  • 一致性 Hash,相同參數的請求總是發到同一提供者。(如果你需要的不是隨機負載均衡,是要一類請求都到一個節點,那就走這個一致性hash策略。)
  • 當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
  • 演算法參見://en.wikipedia.org/wiki/Consistent_hashing
  • 預設只對第一個參數 Hash,如果要修改,請配置 <dubbo:parameter key="hash.arguments" value="0,1" />
  • 預設用 160 份虛擬節點,如果要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />

配置方式

xml 配置方式
服務端服務級別

<dubbo:service interface="..." loadbalance="roundrobin" />
  • 1

客戶端服務級別

<dubbo:reference interface="..." loadbalance="roundrobin" />
  • 1

服務端方法級別

<dubbo:service interface="...">
    <dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:service>
  • 1
  • 2
  • 3

客戶端方法級別

<dubbo:reference interface="...">
    <dubbo:method name="..." loadbalance="roundrobin"/>
</dubbo:reference>
  • 1
  • 2
  • 3

註解配置方式:

消費方基於基於註解的服務級別配置方式:

@Reference(loadbalance = "roundrobin")
HelloService helloService;
  • 1
  • 2

zookeeper宕機與dubbo直連的情況

zookeeper 宕機與 dubbo 直連的情況在面試中可能會被經常問到,所以要引起重視。

在實際生產中,假如 zookeeper 註冊中心宕掉,一段時間內服務消費方還是能夠調用提供方的服務的,實際上它使用的本地快取進行通訊,這只是dubbo健壯性的一種提現。

dubbo的健壯性表現:

  1. 監控中心宕掉不影響使用,只是丟失部分取樣數據
  2. 資料庫宕掉後,註冊中心仍能通過快取提供服務列表查詢,但不能註冊新服務
  3. 註冊中心對等集群,任意一台宕掉後,將自動切換到另一台
  4. 註冊中心全部宕掉後,服務提供者和服務消費者仍能通過本地快取通訊
  5. 服務提供者無狀態,任意一台宕掉後,不影響使用
  6. 服務提供者全部宕掉後,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

我們前面提到過:註冊中心負責服務地址的註冊與查找,相當於目錄服務,服務提供者和消費者只在啟動時與註冊中心交互,註冊中心不轉發請求,壓力較小。所以,我們可以完全可以繞過註冊中心——採用 dubbo 直連 ,即在服務消費方配置服務提供方的位置資訊。

xml配置方式:

<dubbo:reference id="userService" interface="com.zuozewei.gmall.service.UserService" url="dubbo://localhost:20880" />
  • 1

註解方式:

 @Reference(url = "127.0.0.1:20880")   
 HelloService helloService;
  • 1
  • 2

參加文獻:
[1] //dubbo.incubator.apache.org/zh-cn/docs/user/quick-start.html
[2]//github.com/Snailclimb/JavaGuide/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E4%B8%8E%E6%95%B0%E6%8D%AE%E9%80%9A%E4%BF%A1/dubbo.md

 

以上摘自CSDN