1. 程式人生 > >Dubbo 如何成為連線異構微服務體系的最佳服務開發框架

Dubbo 如何成為連線異構微服務體系的最佳服務開發框架

從程式設計開發的角度來說,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 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”