從程式設計開發的角度來說,Apache Dubbo (以下簡稱 Dubbo)首先是一款 RPC 服務架構,它最大的優勢在于提供了面向接口代理的服務程式設計模型,對開發者屏蔽了底層的遠端通信細節。同時 Dubbo 也是一款服務治理架構,它為分布式部署的微服務提供了服務發現、流量排程等服務治了解決方案。
在這篇文章中,我們将以以上基礎能力為背景,嘗試突破 Dubbo 體系自身,探索如何利用 Dubbo 對多協定、多服務發現模型的支援,來實作異構微服務體系間的互聯互通。在實際業務場景中,這可以用來解決異構技術體系共存場景下的通信問題,幫助公司實作在異構技術體系間作平滑遷移,解決大規模跨區域、多叢集部署場景的位址發現及流量排程等問題。
面向接口代理的透明服務開發架構
我們還是從 Dubbo 是一個微服務開發架構 這個大家熟知的概念開始。就像 Spring 是開發 Java 應用的基礎架構一樣,我們經常會選用 Dubbo 作為開發微服務業的基礎架構。Dubbo 架構的最大優勢我認為就在其面向接口的程式設計模型,使得開發遠端服務調用就像開發本地服務一樣(以 Java 語言為例):
1、服務定義
public interface GreetingsService {
String sayHi(String name);
}
2、消費方調用服務
// 和調用本地服務一樣,完全透明。
@Reference
private GreetingService greetingService;
public void doSayHello(String name) {
greetingService.sayHi("Hello world!");
}
下圖是 Dubbo 的基本工作原理圖,服務提供者與服務消費者之間通過注冊中心協調位址,通過約定的協定實作資料交換。
同構/異構微服務體系面臨的問題
關于 Dubbo 協定本身及其服務治理相關功能細節并不是本文的重點,我們今天将從一個更高的層次,來看看公司内部建構微服務體系所面的挑戰,以及 Dubbo 能為架構選型和遷移等提供哪些解決思路。
一個公司内部的微服務可能都是基于某一個相同的服務架構開發的,比如說 Dubbo,對于這樣的架構,我們稱之為是同構的微服務體系;而有些公司的微服務可能是使用多個不同的服務架構所建設,我們稱之為異構的微服務體系,多個不同技術棧微服務體系的共存在大型組織内還是非常普遍的,造成這種局面可能有很多原因。比如,可能是遺留系統帶來的,也可能是公司正在做技術棧遷移,或者就是不同業務部門為了滿足各自特殊需求而做的獨立選型(這也意味着異構微服務體系的長期共存)。
1、異構微服務體系共存
我們很容易想到的一個挑戰是:不同的體系間通常是使用不同的 RPC 通信協定、部署獨立的注冊中心叢集,面對這種多協定、多注冊中心叢集的場景,要如何實作互相之間透明的位址發現和透明的 RPC 調用?如果我們什麼都不做,那麼每個微服務體系就隻能感覺到自己體系内的服務狀态,流量也在各自的體系内封閉。而要做到從體系 A 平滑的遷移到體系 B,或者想長期的保持公司内部多個體系的共存,則解決不同體系間的互聯互通,實作流量的透明排程将是非常重要的環節。
2、Dubbo 體系内部
多協定、多注冊中心叢集的問題在同構的微服務體系中也可能存在,尤其是當一個組織内部的微服務規模增長到一定量級的時候。
- 我們可能要在不同的服務之間采用不同的通信協定,因為不同的服務面臨不同的業務場景,而這也進一步導緻了資料傳輸特點的不同,我們需要分别采用更适合各類業務特點的協定。比如典型的場景:我們可能對于普通的業務服務采用 Dubbo 協定,對于和 FrontEnd 互動的服務需要 HTTP 協定,而對于需要流式資料傳輸的業務則采用 gRPC 協定等等。
- Dubbo 體系内部另一個常出現的問題是,在大規模分布式部署的場景下,微服務系統會做跨區域、跨注冊中心的部署,這個時候就會出現多叢集間位址同步和流量排程的問題。
總結起來,不論是同構體系還是異構體系,都面臨對多協定通信、多注冊中心叢集位址發現的問題。Dubbo 目前是支援多協定、多注冊中心的,可以說就是為解決我們上面分析的 Dubbo 同構體系内的場景而設計的,是以下面我們從同構體系的多協定、多注冊中心場景講起,先了解 Dubbo 多協定、多注冊中心的基本支援情況以及它們是如何工作的。而在後面的一章再進一步探索怎麼擴充這個能力來支援異構微服務體系的互聯互通。
Dubbo 體系内的多協定、多注冊中心機制
我們将通過兩個場景示例,來分别具體的講一下 Dubbo 的多協定、多注冊中心機制的使用方式和工作原理。
多協定
以上是使用 Dubbo 開發的一套微服務,服務間通信使用到了不同的協定,根據我們的調研發現,公司内部啟用多協定其實是非常普遍需求,具體場景在此我們暫不做解釋。
應用 B 作為服務提供者,釋出了 5 個服務,其中:
- DemoService1 DemoService2 通過 dubbo 協定釋出
- DemoService3 DemoService4 通過 gRPC 協定釋出
- DemoService0 通過 dubbo 、gRPC 雙協定釋出
應用 A 作為消費者,使用 dubbo 協定消費 DemoService1 DemoService2,使用 gRPC 協定消費 DemoService0。
應用 B 作為消費者,使用 gRPC 協定消費 DemoService2 DemoService4,使用 dubbo 協定消費 DemoService0。
以下是具體的代碼配置:
1、提供端應用 B
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService1" protocol="dubbo"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService2" protocol="dubbo"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService3" protocol="grpc"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService4" protocol="grpc"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService0" protocol="dubbo, grpc"/>
2、消費端應用 A
<dubbo:reference protocol="dubbo" interface="org.apache.dubbo.samples.basic.api.DemoService1"/>
<dubbo:reference protocol="dubbo" interface="org.apache.dubbo.samples.basic.api.DemoService2"/>
<dubbo:reference protocol="grpc" interface="org.apache.dubbo.samples.basic.api.DemoService0"/>
3、消費端應用C
<dubbo:reference protocol="grpc" interface="org.apache.dubbo.samples.basic.api.DemoService3"/>
<dubbo:reference protocol="grpc" interface="org.apache.dubbo.samples.basic.api.DemoService4"/>
<dubbo:reference protocol="dubbo" interface="org.apache.dubbo.samples.basic.api.DemoService0"/>
Dubbo 多協定支援現狀
Dubbo 目前所支援的協定包括 Dubbo、REST、Thrift、gRPC、JsonRPC、Hessian 等,基本涵蓋了業界大多數主流的 RPC 通信協定。需要注意的是,這些協定的支援都是以直接內建官方 Release 實作的形式來做的,我認為這是一個很好的選擇,既保證了協定解析自身的穩定性,又能使 Dubbo 社群更專注的将更多的精力放在 Dubbo 外圍服務治理能力的改善上。試想如果 Dubbo 社群自己為每個協定提供實作,那是要花費多少精力和時間才能使每種協定達到穩定的生産可用。
除了以上官方提供支援的協定之外,得益于 Dubbo 靈活的擴充機制,想要為 Dubbo 擴充協定非常容易,開發者可以随時為 Dubbo 增加更多的協定支援,包括自有協定擴充。
關于對 gRPC (HTTP/2) 協定的支援,請參閱
《Dubbo 在跨語言和協定穿透性方向的探索:支援 HTTP/2 gRPC》。
多協定能解決的問題
- 将 RPC 架構無縫地接入 Dubbo 的服務治理體系。通過協定擴充将 RPC 協定納入 Dubbo 服務開發體系,進而複用 Dubbo 的程式設計模型和服務發現、流量管控等能力。比如 gRPC,其服務治理體系相對比較弱、程式設計 API 不夠友好,很難直接用于微服務開發。
-
滿足不同場景的調用需求。
各個服務可能是為了滿足不同業務需求而開發,同時外圍消費端應用的技術棧也可能多種多樣,通過啟用不同的通信協定,可以最優化不同場景的通信需求。
-
實作協定間的遷移。
通過支援多種協定,借助注冊中心的協調,可以快速滿足公司内協定遷移的需求。如從自有協定更新到 Dubbo 協定,Dubbo 協定自身更新,從 Dubbo 協定遷移到 gRPC,從 REST 遷移到 Dubbo 協定等。
多注冊中心
當服務叢集規模小的時候,一個中心化的叢集部署方案能很好的解決我們的業務問題。但是随着應用規模的增長、使用者流量的增加,我們就不得不考慮要為業務系統引入跨區域、多叢集的部署方案,而此時同業務系統密切相關的注冊中心叢集也面臨部署方案的選型:
1、繼續維持全局共享的注冊中心叢集。這種架構方案的優點是簡單;缺點是注冊中心叢集由于要儲存全量的位址資料,存儲和推送壓力會變得很大,另外對于一些注冊中心産品(如 Zookeeper 等)在跨叢集網絡部署的場景下穩定性和性能可能都會面臨挑戰。
2、每個業務叢集部署獨立的注冊中心叢集。多注冊中心叢集的優點是能解決跨叢集網絡可用性的問題,同時也能夠減輕注冊中心的存儲和推送壓力;缺點則是要求服務架構(如 Dubbo 等)能有同時釋出/監聽多個注冊中心叢集的能力。
下面我們具體看一下,Dubbo 為多注冊中心叢集場景提供的解決方案。
上圖有兩個業務叢集,分别部署在北京和上海,每個業務叢集有自己獨立的注冊中心叢集,要解決兩個業務叢集間服務的透明 RPC 通信問題。
1、服務提供端,雙注冊中心釋出
<dubbo:registry id="beijingRegistry" address="zookeeper://${zookeeper.address1}" default="false"/>
<dubbo:registry id="shanghaiRegistry" address="zookeeper://${zookeeper.address2}" />
<dubbo:service interface="org.apache.dubbo.samples.multi.registry.api.HelloService" ref="helloService" registry="shanghaiRegistry,beijingRegistry"/>
<dubbo:service interface="org.apache.dubbo.samples.multi.registry.api.DemoService" ref="demoService" registry="shanghaiRegistry,beijingRegistry"/>
2、服務消費端,根據消費需求做單/雙注冊中心訂閱
<dubbo:registry id="beijingRegistry" address="zookeeper://${zookeeper.address1}" default="false" preferred="true" weight="100"/>
<dubbo:registry id="shanghaiRegistry" address="zookeeper://${zookeeper.address2}" default="true" weight="20"/>
<dubbo:reference interface="org.apache.dubbo.samples.multi.registry.api.DemoService"/>
<dubbo:reference interface="org.apache.dubbo.samples.multi.registry.api.DemoService" registry="beijingRegistry, shanghaiRegistry"/>
<dubbo:reference interface="org.apache.dubbo.samples.multi.registry.api.HelloService" registry="beijingRegistry"/>
<dubbo:reference interface="org.apache.dubbo.samples.multi.registry.api.HelloService" registry="shanghaiRegistry,shanghaiRegistry"/>
Dubbo 對異構注冊中心叢集的支援
雖然我們會做多注冊中心叢集部署,但通常情況下,我們部署的都是相同的注冊中心産品,如都是 Zookeeper、Nacos;而對于注冊中心遷移的場景,則要求 Dubbo 能提供對更多的注冊中心産品的支援,或者最重要的是要有很好的擴充能力。Dubbo 官方目前支援的注冊中心實作有:
這裡需要特别提到的一點是,目前 Dubbo 的服務注冊/發現模型是以接口為粒度的,而從 2.7.5 版本開始,Dubbo 新引入了應用粒度的服務注冊/發現模型。這一方面有助于優化 Dubbo 目前服務發現機制、提升服務容量,另一方面對于聯通以 SpringCloud 為代表的微服務體系也非常重要(關于這點在下一章中有進一步提及)。更多關于《應用粒度服務發現:服務自省》的介紹,我們将在接下來的文章或文檔中予以補充,請持續關注。
多訂閱帶來的流量排程問題
在引入多注冊中心叢集後,Dubbo 在流量選址時的多了一層注冊中心叢集間的負載均衡:
在 Cluster Invoker 這一級,我們支援的選址政策有(2.7.5+ 版本,具體使用請參見文檔):
1、指定優先級
<!-- 來自 preferred=“true” 注冊中心的位址将被優先選擇,隻有該中心無可用位址時才 Fallback 到其他注冊中心 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />
2、同 zone 優先
<!-- 選址時會和流量中的 zone key 做比對,流量會優先派發到相同 zone 的位址 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />
3、權重輪選
<!-- 來自北京和上海叢集的位址,将以 10:1 的比例來配置設定流量 -->
<dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight=”100“ />
<dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight=”10“ />
4、預設,stick to 任意可用
多注冊中心适用的場景
-
同區域流量優先排程
出于容災或者服務伸縮性需求,服務/應用往往需要部署在多個獨立的機房/區域,在每個區域有獨立注冊中心叢集的場景下,實作同區域的流量優先排程就能很好的解決延遲和可用性問題。
-
注冊中心遷移
公司的服務一直以來可能是存儲在某一個注冊中心,如 Zookeeper,但到了某個時間節點,因為各種各樣的原因,當我們要遷移到另外的注冊中心時,多注冊中心模型能夠保證平滑的遷移。
-
異構系統互通
不同微服務體系開發的服務,都封閉在各自的服務發現體系中,而通過統一的多注冊中心模型,可以實作不同體系的服務互相發現。
借助 Dubbo 聯通異構的微服務體系
上文我們提到了在組織記憶體在異構微服務體系的各種合理可能性,現在我們來具體看一下異構微服務體系的實際場景,以及使用 Dubbo 實作互聯互通的解決方法。首先我們先通過一張圖來看一下,聯通異構的微服務體系具體是一個什麼樣的場景。
如上圖所示,我們有部分微服務可以是基于 SpringCloud、gRPC、K8S 或者是自建體系建構的,他們各自之間預設是互相隔離無法聯通的。當我們再建構一套基于 Dubbo 的微服務體系時,則利用 Dubbo 的多協定、多服務發現模型,我們就可以做到和各個微服務體系間的兩兩之間的互聯互通。進一步的,如圖中橙色箭頭所示,依賴 Dubbo 體系作為橋接層,我們還可以實作兩個異構微服務體系間的打通。
對于以下幾個示例場景,由于在位址發現層面目前沒有統一的标準,我們暫且假設位址發現層面不同的體系建是沒有障礙的,我們将重點關注遷移的基本流程以及通信協定環節。(關于位址發現部分,我們将在後續《服務自省:基于應用粒度的服務發現》之後再深入探讨)
Dubbo 體系内的協定遷移(共存)
絕大多數開發者對 Dubbo 有這麼一個固有認知:使用 Dubbo 開發微服務系統,則就要用 Dubbo 協定來作為服務間的通信協定才是最優方案。實際上,我們完全沒有必要隻束縛在 Dubbo RPC 協定上。Dubbo 作為微服務開發架構和 Dubbo 作為 RPC 協定這是兩個概念,其實是完全可以分開來看待的,比如我們用 Dubbo 架構開發的業務系統,選用 rest、gRPC 通信是完全沒有問題的(參加 Dubbo 支援的協定清單),具體用什麼協定根據業務特點和技術規劃才是最适合的。
目前在雲原生、Mesh 的大背景下, HTTP1/2、gRPC 協定開始受到越來越多的關注,一方面原因自然是因為它們在标準化方面做的更好,得到的更多的網絡裝置和基礎設施的支援,具備更好的通用性和穿透性。對于很多有雲原生遷移意願的企業來說,往此類協定遷移無疑将對之後的架構更新有更多的幫助。
下圖示範了在 Dubbo 體系内,從 Dubbo 協定向 gRPC 協定遷移的一個中間狀态。
- 最左邊的代表尚未遷移的老應用,這類應用在遷移過程中仍然要消費和提供 Dubbo 協定的服務。
- 中間的代表處于遷移中的應用,他們中間可能有些是服務提供者,既要為左邊的老系統提供提供 Dubbo 協定服務;又要為右邊的新系統提供 gRPC 服務;是以他們都是雙協定暴露服務。
- 最右邊則代表是新開發的或者已經遷移完成的應用,這個體系内已能完全用 gRPC 協定通信。
- 最終度過中間态後,我們期望所有的應用都達到最左邊應用的狀态,實作完全的 gRPC 協定通信。
Spring Cloud 體系遷移到 Dubbo 體系(共存)
如前文所述,由于 SpringCloud 和 Dubbo 間服務發現模型的問題,要兩個體系間的位址互通需要 Dubbo 側作相應的适配,關于這部分内容将在接下來的 2.7.5 版本《服務自省》部分釋出,在此我們暫且認為已經打通。
Dubbo 體系内的部分應用作為透明的聯通兩個體系的關鍵節點,部分服務提供者應用要雙協定釋出、部分消費者應用要做到標明協定消費。由于老的 Spring Cloud 體系不允許做任何改動,是以聯通兩套體系的關鍵是 REST 協定,對 Dubbo 側的應用來說:
- 部分應用可能要以 REST 協定消費 SpringCloud 的服務;
- 部分應用可能要暴露 REST 協定共 SpringCloud 消費;
- Dubbo 自有體系内則通過自己標明的協定通信,這裡就比較靈活了,可以是 Dubbo、REST、gRPC 等其中的任一種。而如果標明 REST 協定則對于與 SpringCloud 體系的聯通就變得更加自然了,因為兩端的協定都是統一的。
對于消費 Spring Cloud 服務的應用,要配置服務 :
<dubbo:reference interface ="xxx.SpringService" protocol="rest"/>
對于提供服務給 Spring Cloud 側消費的應用,則指定服務暴露為 rest 協定,或者雙協定暴露(因如果這個服務還要被新體系内的應用調用到):
<dubbo:service interface="xxx.NewService" protocol="rest,dubbo"/>
作為 Dubbo 的維護者,雖然我們這裡有明顯的偏向性,講的是從如何從 SpringCloud 體系遷移到 Dubbo 體系。但是反過來考慮,如果你已經或者即将選型 Dubbo 來開發微服務,則未來從 Dubbo 遷移到 SpringCloud 也是同樣的思路,Dubbo 的多協定、多注冊模型為雙向遷移都提供了同樣的靈活性。
自建體系遷移到 Dubbo 體系(共存)
這個場景和上一節中講到的的 SpringCloud 遷移有些類似,最大的差別在于 rest 協定是 Dubbo 官方預設提供支援的,而對于已有的微服務體系内的私有通信協定,則需要先要自己去擴充 Dubbo Protocol 來提供協定層面的支援,關于 Protocol 如何擴充請參見以下官方文檔:
http://dubbo.apache.org/zh-cn/docs/dev/impls/protocol.html總結與展望
要實作異構微服務體系間的共存或遷移,關鍵點在打通異構體系間的協定與服務發現,得益于 Dubbo 自身對多協定、多注冊模型的支援,我們可以很容易的使 Dubbo 成為橋接異構微服務體系的中間層。熟悉 Dubbo 多協定實作細節的同學,可能會擔心在服務數量較多的場景下,多協定注冊會導緻位址數量翻倍進而影響位址推送性能。
另外,在本文“借助 Dubbo 聯通異構的微服務體系”一節中,關于如何實作異構體系間的透明服務發現部分,我們沒有做詳細的說明。涉及服務發現的這部分,我們将在接下來的文章中做具體闡述,看看 Dubbo 2.7.5 版本引入新的服務發現機制是如何解決這個問題的,請持續關注後續文章及 Dubbo 官方文檔。
作者資訊:
劉軍,GitHub 賬号 Chickenlj,Apache Dubbo PMC,項目核心維護者,見證了 Dubbo 從重新開機開源到 Apache 畢業的整個流程。現任職阿裡雲雲原生應用平台團隊,參與服務架構、微服務相關工作,目前主要在推動 Dubbo 開源的雲原生化。
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”