天天看點

Apache Dubbo服務自省架構設計與實作

以下是精彩視訊内容整理:

一、服務自省架構背景

第一個議題就是關于服務自省架構的背景,它離不開我們的技術的一個引進,其中包括三個方面。第一個是Cloud-Native的技術的興起,所謂的雲原生的技術興起,這個技術相信大家已經耳熟能詳。

Apache Dubbo服務自省架構設計與實作

第二方面是關于Java社群裡面的一個龍頭老大,Spring Cloud的快速崛起。它其實是目前很多中小型企業首選的一個微服務架構,或者說雲原生的開發架構。它能夠快速的幫我們整合一些相關的服務,并且用統一的API的方式來抹去我們的一些廠商約定的技術。

Apache Dubbo服務自省架構設計與實作

第三個方面是Dubbo所面臨的挑戰。第一,如何解決或緩解注冊中心壓力過載。包括記憶體壓力,網絡壓力和通知壓力。第二,如何支援以應用為粒度的服務注冊與發現。包括支援Spring Cloud服務注冊與發現模型,支援Kubernetes服務注冊與發現模型和相容Dubbo傳統服務注冊與發現模型。第三,如何精簡Dubbo URL中繼資料,如下圖所示。

Apache Dubbo服務自省架構設計與實作

二、服務自省架構定義

(一)定義

我們再來看下個大的議題,就是關于服務自省這個架構我們如何來定義。實際上關于自省有很多種講法,通常我們說自省是自我檢討,嚴格意義上講,它不應該叫自我檢討,應該叫透視的一個功能,相當于x光一樣的給你來做一個全面的審視。Dubbo應用在運作時處理和分析Dubbo服務元資訊的過程,如目前應用暴露的Dubbo服務以及各自的通訊協定等。期間會伴随着事件的廣播和處理,如服務暴露事件。

Apache Dubbo服務自省架構設計與實作
(二)術語約定

-Service:SOA 或微服務中的“服務”,或稱之為“應用”,具有全局唯一的名稱

-Service Name: 服務名稱,或應用名稱

-Servce Instance:服務執行個體,或稱為應用執行個體(Application Instance),表示單個 Dubbo 應用程序

-Registry:注冊中心

-Dubbo 服務:又稱之為“Dubbo 業務服務”,包含 Java 接口、通訊協定,版本(version)和分組(group)等元資訊

-Dubbo 服務 ID:唯一鑒定 Dubbo 服務的中繼資料,用于 Dubbo 服務暴露(釋出)和訂閱

-Provider:Dubbo 服務提供方

-Consumer:Dubbo 服務消費方

-Dubbo 服務暴露:也稱之為 Dubbo 服務釋出,或英文中的“export”、”exported”

-Dubbo 應用服務:也稱之為 Dubbo 業務服務,或業務 Dubbo 服務

三、服務自省架構使用場景

(一)超大規模 Dubbo 服務治理場景

如果 Dubbo 叢集規模超過一千以上,或者叢集擴縮容已無法自如地執行,如 Zookeeper 管理數萬 Dubbo 服務,服務自省可極大化減輕注冊中心的壓力,尤其在記憶體足迹、網絡傳輸以及變更通知上展現。

(二)微服務架構和雲原生應用

如果想要 Dubbo 應用更好地微服務化,或者更接近于雲原生應用,那麼服務自省是一種不錯的選擇,它能夠提供已應用為粒度的服務注冊與發現模型,全面地支援最流行的 Spring Cloud 和 Kubernetes 注冊中心,并且能與 Spring Cloud 或 Spring Boot 應用互動。

(三)Dubbo 中繼資料架構的基石

Dubbo 中繼資料架構是圍繞 Dubbo DevOps 而引入,包括 Dubbo 配置中繼資料(如:屬性配置、路由規則等)和結構中繼資料(如:Java 注解、接口和文檔等)。服務自省作為 Dubbo 中繼資料的基礎設施,不僅支援所有中繼資料的存儲和平滑更新,而且不會對注冊中心、配置中心和中繼資料中心産生額外的負擔。

四、服務自省架構設計

傳統架構

Apache Dubbo 是一款面向接口代理的高性能 RPC 架構,提供服務注冊與發現的特性,其基礎架構如下圖所示:

Apache Dubbo服務自省架構設計與實作

(圖 1)

架構上,Dubbo 服務自省不僅要解決上述挑戰,而且實際場景則更為複雜,是以,架構細節也将循序漸進地展開讨論,整體架構可由以下子架構組成:

-服務注冊與發現架構

-中繼資料服務架構

-事件驅動架構

服務注冊與發現架構

Dubbo 服務自省首要需求是減輕注冊中心的承載的壓力,同時,以應用為粒度的服務注冊與發現模型不但能夠最大化的減少 Dubbo 服務元資訊注冊數量,而且還能支援 Spring Cloud 和 Kubernetes 環境,可謂是一舉兩得,架構圖如下所示:

Apache Dubbo服務自省架構設計與實作

(圖 2)

注冊實體

圖中所示,從 Provider 和 Consumer 向注冊中心注冊的實體不再是 Dubbo URL,而是服務執行個體(Service Instance),一個服務執行個體代表一個 Provider 或 Consumer Dubbo 應用程序。服務執行個體屬性包括:

-服務名(Service Name):該名稱必須在注冊中心全局唯一

注:名稱規則架構上不做限制,不過不同注冊中心的規則存在差異

-主機位址(Host/IP):能夠被解析的主機名或者 TCP IP 位址

-服務端口(Port):應用程序所暴露的 Dubbo 協定端口,如 Dubbo 預設端口 20880

注:如果應用程序暴露多個 Dubbo 協定端口,如 dubbo 和 rest,那麼,服務端口随機挑選其一,架構上不強制檢驗端口是否可用

-中繼資料(Metadata):服務執行個體的附加資訊,用于存儲 Dubbo 元資訊,類似于通訊協定頭或附件

-激活狀态(Enabled):用于标記目前執行個體是否對外提供服務

上述服務執行個體模型的支援依賴于注冊中心的實作。換言之,并非所有注冊中心實作滿足服務自省架構的要求。

注冊中心

除了滿足服務執行個體模型的要求之外,注冊中心還得具備以下能力:

-服務執行個體變化通知(Notification):如上圖步驟 4 所示,當 Consumer 訂閱的 Provider 的服務執行個體發生變化時,注冊中心能夠實時地通知 Consumer

-心跳檢測(Heartbeats):注冊中心能夠檢測失效的服務執行個體,并且合理地移除它們

業界主流的注冊中心中滿足上述要求的有:

-Apache Zookeeper

-HashiCorp Consul

-Netflix Eureka

-Alibaba Nacos

-Kubernetes API Server

總之,Spring Cloud 與 Kubernetes 注冊中心均符合服務自省對注冊中心的要求。不過,在 Dubbo 傳統 RPC 使用場景中,Provider 和 Consumer 關注的是 Dubbo 服務接口,而非 Service 或服務執行個體。假設需要将現有的 Dubbo 應用遷移至服務自省架構,Provider 和 Consumer 做大量的代碼調整是不現實的。理想的情況下,兩端實作代碼均無變化,僅修改少量配置,就能達到遷移的效果。那麼,Dubbo 服務接口是如何與 Service 進行映射的呢?

Dubbo 服務與 Service 映射

前文曾讨論,單個 Dubbo Service 能夠釋出多個 Dubbo 服務,是以,Dubbo 服務與 Service 的數量關系是 N 對 1。不過,Dubbo 服務與 Dubbo Service 之間并不存在強綁定關系,換言之,某個 Dubbo 服務也能部署在多個 Dubbo Services 中,是以,Dubbo 服務與 Service 數量關系是 N 對 M(N, M >= 1),如下圖所示:

Apache Dubbo服務自省架構設計與實作

(圖 3)

上圖中 P1 Service 到 P3 Service 為 Dubbo Service,com.acme.Interface1 到 com.acme.InterfaceN 則為 Dubbo 服務接口全稱限定名(QFN)。值得注意的是,Dubbo 服務的 Java 接口(interface)允許不同的版本(version)或分組(group),是以僅憑 Java 接口無法唯一辨別某個 Dubbo 服務,還需要增加通訊協定(protocol)方可,映射關系更新如下:

Apache Dubbo服務自省架構設計與實作

(圖 4)

Dubbo 服務 ID 字元表達模式為: ${protocol}:${interface}:${version}:${group} , 其中,版本(version)或分組(group)是可選的。當 Dubbo Consumer 訂閱 Dubbo 服務時,建構對應 ID,通過這個 ID 來查詢 Dubbo Provider 的 Service 名稱清單。

由于 Dubbo 服務與 Service 的映射關系取決于業務場景,架構層面無從預判。是以,這種映射關系隻能在 Dubbo 服務暴露時(運作時)才能确定,否則,Dubbo 服務能被多個 Consumer 應用訂閱時,Consumer 無法定位 Provider Service 名稱,進而無法完成服務發現。同時,映射關系的資料通常采用配置的方式來存儲,服務自省提供兩種配置實作,即 “中心化映射配置” 和 “本地化映射配置”。

中心化映射配置

明顯地,注冊中心來扮演動态映射配置的角色并不适合,不然,Dubbo Service 與映射關系在注冊中心是平級的,無論在了解上,還是設計上是混亂的。結合 Dubbo 現有基礎設施分析,這個存儲設施可由 Dubbo 配置中心承擔。

其中 Dubbo 2.7.5 動态配置 API(DynamicConfiguration )支援二級結構,即:group 和 key,其中,group 存儲 Dubbo 服務 ID,而 key 則關聯對應的 Dubbo Service 名稱,對應的 “圖 4” 的資料結構則是:

Apache Dubbo服務自省架構設計與實作

(圖 5)

如此設計的原因如下:

1.擷取 Dubbo 服務對應 Services

利用 DynamicConfiguration#getConfigKeys(String group) 方法,能夠輕松地通過 Dubbo 服務 ID 擷取其釋出的所有 Dubbo Services,結合服務發現接口擷取服務所部署的 Service 執行個體集合,最終轉化為 Dubbo URL 清單。

1.避免 Dubbo Services 配置互相覆寫

以 Dubbo 服務 ID dubbo:com.acme.Interface1:default 為例,它的提供者 Dubbo Services 分别:P1 Service 和 P2 Service。假設配置 Group 為 “default”(任意名字均可), Key 為 “dubbo:com.acme.Interface1:default”,而内容則是 Dubbo Service 名稱的話。當 P1 Service 和 P2 Service 同時啟動時,無論哪個 Services 最後完成 Dubbo 服務暴露,那麼,該配置内容必然是二選其一,無論配置中心是否支援原子操作。即使配置中心支援内容追加的特性,由于兩個 Service 服務執行個體過程不确定,配置内容可能會出現重複,如:“P1 Service,P2 Service,P1 Service”。

1.擷取 Dubbo 服務釋出的 timestamp

配置中心潛在的壓力

假設當 P1 Service 存在 5 個服務執行個體,當 Dubbo 服務 dubbo:com.acme.Interface1:default(ID)釋出時,配置所關聯的 key 就是目前 Dubbo Service 名稱,即(P1 Service),而内容則是最後釋出該 Dubbo 服務的時間戳(timestamp)。當服務執行個體越多時,配置中心和網絡傳輸所承受的寫入壓力也就越大。當然架構設計上,服務自省也希望避免重複推送配置,比如在 DynamicConfiguration API 增加類似于 publishConfigIfAbsent 這樣的方法,不過目前大多數配置中心産品(如:Nacos、Consul)不支援這樣的操作,是以未來服務自省架構會有針對性的提供支援(如:Zookeeper)。

注冊中心作為配置中心

由于服務自省架構必須依賴注冊中心,同時動态映射配置又依賴配置中心的話,應用的架構複雜度和維護成本均有所提升,不過 Apache Dubbo 所支援的部分注冊中心也可作為配置中心使用,情況如下所示:

Apache Dubbo服務自省架構設計與實作

本地化映射配置

如果開發人員認為配置中心的引入增加了架構的複雜性,那麼,靜态映射配置或許是一種解決方案。

該特性并未在最新 Dubbo 2.7.6 全面釋出,部分特性已在 Dubbo Spring Cloud 中釋出

接口映射配置

在 Dubbo 傳統的程式設計模型中, 常以 Java 注解 @Reference 或 XML 元素 `` 訂閱目标 Dubbo 服務。服務自省架構在此基礎上增加 service 屬性的映射一個或多個 Dubbo Service 名稱,如:

Apache Dubbo服務自省架構設計與實作

如此配置後,Dubbo 服務 com.acme.Interface1 将向 p1-service 和 p2-service 訂閱服務。如果開發人員認為這種方式會侵入到代碼,服務自省還提供外部化配置方式配置映射。

外部化映射配置

服務自省架構支援外部化配置的方式聲明“Dubbo 服務與 Service 映射”,配置格式為 Properties ,以圖 4 為例,内容如下:

Apache Dubbo服務自省架構設計與實作
Apache Dubbo服務自省架構設計與實作

(圖 6)

不過,映射關系并非是一種強限制,Dubbo Provider 的服務是否可用的檢驗方法是探測目标 Dubbo Service 是否存在,并需确認訂閱的 Dubbo 服務在目标 Services 是否真實暴露,是以,服務自省引入了 Dubbo 中繼資料服務架構,來完成 Dubbo 服務 URL 的存儲。

中繼資料服務架構

Dubbo 中繼資料服務是一個正常的 Dubbo 服務,為服務訂閱端提供 Dubbo 中繼資料的服務目錄,類似于 WebServices 中的 WDSL 或 REST 中的 HATEOAS,幫助 Dubbo Consumer 擷取訂閱的 Dubbo 服務的 URL 清單。中繼資料服務架構無法獨立于服務注冊與發現架構而存在,下面通過“整體架構”的讨論,了解兩者之間的關系。

整體架構

架構上,無論 Dubbo Service 屬于 Provider 還是 Consumer,甚至是兩者的混合,每個 Dubbo (Service)服務執行個體有且僅有一個 Dubbo 中繼資料服務。換言之,Dubbo Service 不存在純粹的 Consumer,即使它不暴露任何業務服務,那麼它也可能是 Dubbo 運維平台(如 Dubbo Admin)的 Provider。不過出于行文的習慣,Consumer 仍舊被定義為 Dubbo 服務消費者(應用)。由于每個 Dubbo Service 均釋出自身的 Dubbo 中繼資料服務,那麼,架構不會為不同的 Dubbo Service 設計獨立的中繼資料服務接口(Java)。換言之,所有的 Dubbo Service 中繼資料服務接口是統一的,命名為 MetadataService 。

微觀架構

從 Dubbo 服務(URL)注冊與發現的視角, MetadataService 扮演着傳統 Dubbo 注冊中心的角色。綜合服務注冊與發現架構(Dubbo Service 級别),微觀架構如下圖所示:

Apache Dubbo服務自省架構設計與實作

(圖 7)

對于 Provider(服務提供者)而言,Dubbo 應用服務暴露與傳統方式無異,而 MetadataService 的暴露時機必須在它們完成後,同時, MetadataService 需要收集這些 Dubbo 服務的 URL(存儲細節将在“中繼資料服務存儲模式“ 小節讨論)。假設某個 Provider 的 Dubbo 應用服務暴露數量為 N,那麼,它所有的 Dubbo 服務暴露數量為 N + 1。

對于 Consumer(服務消費者)而言,獲 Dubbo 應用服務訂閱 URL 清單後,Dubbo 服務調用的方式與傳統方式是相同的。不過在此之前,Consumer 需要通過 MetadataService 合成訂閱 Dubbo 服務的 URL。該過程之是以稱之為“合成”,而非“擷取,是因為一次 MetadataService 服務調用僅在其 Provider 中的一台服務執行個體上執行,而該 Provider 可能部署了 N 個服務執行個體。具體“合成”的細節需要結合“宏觀架構”來說明。

宏觀架構

中繼資料服務的宏觀架構依賴于服務注冊與發現架構,如下圖所示:

Apache Dubbo服務自省架構設計與實作

(圖 8)

圖 8 中 p 和 c 分别代表 Provider 和 Consumer 的執行動作,後面緊跟的數字表示動作的次序,從 0 開始計數。執行動作是串行的,并屬于 Fast-Fail 設計,如果前階段執行失敗,後續動作将不會發生。之是以如此安排是為了確定 MetadataService 能夠暴露和消費。首先從 Provider 執行流程開始說明。

Provider 執行流程

p0:釋出所有的 Dubbo 應用服務,聲明和定義方式與傳統方式完全相同。

p1:暴露 MetadataService ,該步驟完全由架構自行處理,無論是否 p0 是否暴露 Dubbo 服務

p2:在服務執行個體注冊之前, 架構将觸發并處理事件(Event),将 MetadataService 的中繼資料先同步到服務執行個體(Service Instance)的中繼資料。随後,執行服務執行個體注冊

p3:建立所有的 Dubbo 應用服務與目前 Dubbo Service 名稱的映射,并同步到配置源(抽象)

Consumer 執行流程

c0:注冊目前 Dubbo Service 的服務執行個體,可選步驟,架構允許 Consumer 不進行服務注冊

c1:通過訂閱 Dubbo 服務元資訊查找配置源,擷取對應 Dubbo Services 名稱(清單)

c2:利用已有 Dubbo Service 名稱(可能存在多個),通過服務發現 API 擷取 Provider 服務執行個體集合。假設 Service 名稱 P,服務執行個體數量為 N

c3:

a.随機選擇 Provider 一台服務執行個體 Px,從中擷取 MetadataService 的中繼資料

b.将中繼資料組裝 MetadataService Dubbo 調用用戶端(代理)

c.發起 MetadataService Dubbo 調用,擷取該服務執行個體 Px 所暴露的 Dubbo 應用服務 URL 清單

d.從 Dubbo 應用服務 URL 清單過濾出目前訂閱 Dubbo 應用服務的 URL

e.理論上,步驟 c 和 d 還需要執行 N-1 次才能擷取 P 所有服務執行個體的 Dubbo URL 清單。為了減少調用次數,步驟 d 的結果作為模闆,克隆其他 N-1 台服務執行個體 URL 清單

f.将所有訂閱 Dubbo 應用服務的 URL 同步到 Dubbo 用戶端(與傳統方式是相同的)

c4:發起 Dubbo 應用服務調用(與傳統方式是相同的)

不難看出,上述架構以及流程結合了“服務注冊與發現”與“中繼資料服務”雙架構,步驟之間會觸發相關 Dubbo 事件,如“服務執行個體注冊前事件”等。換言之,三種架構綜合體也就是服務自省架構。

至此,關于 Dubbo 服務自省架構設計方面,還存在一些細節亟待說明,比如:

1.不同的 Dubbo Service 的 MetadataService 怎樣展現差異呢?

2.MetadataService 作為一個正常的 Dubbo 服務,它的注冊元資訊存放在何處?

3.MetadataService 作為服務目錄,它管理的 Dubbo 應用服務 URL 是如何存儲的?

4.在 Consumer 執行流程的 c3.e 中,克隆 N - 1 條 URL 的前提是該 Provider 的所有服務執行個體均部署了相同 Dubbo 應用服務。如果 Provider 處于更新的部署過程,同一 Dubbo 應用服務接口在不同的服務執行個體上存在差異,那麼該服務的 URL 應該如何擷取?

5.除了 Dubbo 服務 URL 發現之外,中繼資料服務還支援哪些中繼資料類型呢?

中繼資料服務 Metadata

中繼資料服務 Metadata,稱之為“中繼資料服務的中繼資料”,主要包括:

inteface:Dubbo 中繼資料服務所暴露的接口,即 MetadataService

serviceName : 目前 MetadataService 所部署的 Dubbo Service 名稱,作為 MetadataService 分組資訊

group:目前 MetadataService 分組,資料使用 serviceName

version:目前 MetadataService 的版本,版本号通常在接口層面聲明,不同的 Dubbo 發行版本 version 可能相同,比如 Dubbo 2.7.5 和 2.7.6 中的 version 均為 1.0.0。理論上,version 版本越高,支援元資訊類型更豐富

protocol: MetadataService 所暴露協定,為了確定 Provider 和 Consumer 通訊相容性,預設協定為:“dubbo”,也可以支援其他協定。

port:協定所使用的網絡端口

host:目前 MetadataService 所在的服務執行個體主機或 IP

params:目前 MetadataService 暴露後 URL 中的參數資訊

不難得出,憑借以上中繼資料服務的 Metadata,可将中繼資料服務的 Dubbo 服務 ID 确定,輔助 Provider 服務暴露和 Consumer 服務訂閱 MetadataService 。不過對于 Provider,這些元資訊都是已知的,而對 Consumer 而言,它們直接能擷取的元資訊僅有:

serviceName:通過“Dubbo 接口與 Service 映射”關系,可得到 Provider Service 名稱

interface:即 MetadataService ,因為 Provider 和 Consumer 公用 MetadataService 接口

group:即 serviceName

不過 Consumer 合成 MetadataService Dubbo URL 還需擷取 version、host、port、protocol 以及 params:

version:盡管 MetadataService 接口是統一接口,然而 Provider 和 Consumer 可能引入的 Dubbo 版本不同,進而它們使用的 MetadataService version 也會不同,是以這個資訊需要 Provider 在暴露MetadataService 時,同步到服務執行個體的 Metadata 中,友善 Consumer 從 Metadata 中擷取

host:由于 Consumer 已得到 serviceName,可通過服務發現 API 擷取服務執行個體對象,該對象包含 host 屬性,直接被 Consumer 擷取即可。

port:與 version 類似,從 Provider 服務執行個體中的 Metadata 中擷取

params:同上

通過中繼資料服務 Metadata 的描述,解釋了不同 Dubbo Services 是怎樣展現差異性的,并且說明了 MetadataService 元資訊的存儲媒體,這也就是服務自省架構為什麼強依賴支援 Metadata 的注冊中心的原因。下個小節将讨論 MetadataService 所存儲 Dubbo 應用服務 URL 存放在何處。

中繼資料服務存儲模式

Dubbo 2.7.5 在引入 MetadataService 的同時,也為其設計了兩種存儲方式,适用于不同的場景,即“本地存儲模式”和“遠端存儲模式”。其中,本地存儲模式是預設選項。

中繼資料服務本地存儲模式

本地存儲模式又稱之為記憶體存儲模式(In-Memory),如 Dubbo 應用服務發現和注冊場景中,暴露和訂閱的 URL 直接存儲在記憶體中。架構上,本地存儲模式的 MetadataService 相當于去中心化的 Dubbo 應用服務的注冊中心。

中繼資料服務遠端存儲模式

遠端存儲模式,與去中心化的本地存儲模式相反,采用 Dubbo 中繼資料中心來管理 Dubbo 元資訊,又稱之為元中心化存儲模式(Metadata Center)。

選擇存儲模式

為了減少負載壓力和維護成本,服務自省中的中繼資料服務推薦使用“本地存儲模式”。

回顧前文“Consumer 執行流程”中的步驟 c3.e,為了減少 MetadataService 調用次數,服務自省将第一次的調用結果作為模闆,再結合其他 N-1 服務執行個體的元資訊,合成完整的 N 台服務執行個體的 Dubbo 元資訊。假設,Dubbo Service 服務執行個體中部署的 Dubbo 服務數量和内容不同,那麼,c3.e 的執行步驟是存在問題的。是以,服務自省引入“Dubbo 服務修訂版本”的機制來解決不對等部署的問題。

盡管“Dubbo 服務修訂版本”機制能夠介紹 MetadataService 整體消費次數,然而當新修訂版本的服務執行個體過少,并且 Consumer 過多時,如新的版本 Provider 應用分批部署,每批的服務執行個體為 1 台,而其 Consumer 服務執行個體成千上萬。為了確定這類場景的穩定性,Provider 和 Consumer 的 MetadataService 可選擇“遠端存儲模式”,避免消費熱點的發生。

Dubbo 服務修訂版本

當業務出現變化時,Dubbo Service 的 Dubbo 服務也會随之更新。通常,Provider 先行更新,Consumer 随後跟進。

考慮以下場景,Provider “P1” 線上已釋出 interface 為 com.acme.Interface1,group 為 group , version 為 v1 ,即 Dubbo 服務 ID 為:dubbo:com.acme.Interface1:v1:default 。P1 可能出現更新的情況有:

1.Dubbo 服務 interface 更新

由于 Dubbo 基于 Java 接口來暴露服務,同時 Java 接口通常在 Dubbo 微服務中又是唯一的。如果 interface 的全類名調整的話,那麼,相當于 com.acme.Interface1 做下線處理,Consumer 将無法消費到該 Dubbo 服務,這種情況不予考慮。如果是 Provider 新增服務接口的話,那麼 com.acme.Interface1 則并沒有變化,也無需考慮。是以,有且僅有一種情況考慮,即“Dubbo interface 方法聲明更新”,包括:

增加服務方法

删除服務方法

修改方法簽名

1.Dubbo 服務 group、version 和 protocol 更新

假設 P1 在更新過程中,新的服務執行個體部署僅存在調整 group 後的 Dubbo 服務,如 dubbo:com.acme.Interface1:v1:test ,那麼這種更新就是不相容更新,在新老交替過程中,Consumer 僅能消費到老版本的 Dubbo 服務。當新版本完全部署完成後,Consumer 将無法正常服務調用。如果,新版本中 P1 同時部署了 dubbo:com.acme.Interface1:v1:default

和 dubbo:com.acme.Interface1:v1:test 的話,相當于 group 并無變化。同理,version 和 protocol 變化,相當于 Dubbo 服務 ID 變化,這類情況無需處理。

1.Dubbo 服務中繼資料更新

這是一種比較特殊的更新方法,即 Provider 所有服務執行個體 Dubbo 服務 ID 相同,然而 Dubbo 服務的參數在不同版本服務執行個體存在差異,假設 Dubbo Service P1 部署 5 台服務,其中 3 台服務執行個體設定 timeout 為 1000 ms,其餘 2 台 timeout 為 3000 ms。換言之,P1 擁有兩個版本(狀态)的 MetadataService 。

綜上所述,無論是 Dubbo interface 方法聲明更新,還是 Dubbo 服務中繼資料更新,均可認為是 Dubbo 服務更新的因子,這些因子所計算出來的數值稱之為“Dubbo 服務修訂版本”,服務自省架構将其命名為“revision”。架構設設計上,當 Dubbo Service 增加或删除服務方法、修改方法簽名以及調整 Dubbo 服務中繼資料,revision 也會随之變化,revision 資料将存放在其 Dubbo 服務執行個體的 metadata 中。當 Consumer 訂閱 Provider Dubbo 服務元資訊時,MetadataService 遠端調用的次數取決于服務執行個體清單中出現 revision 的個數,整體執行流程如下圖所示:

Apache Dubbo服務自省架構設計與實作

(圖 9)

1.Consumer 通過服務發現 API 向注冊中心擷取 Provider 服務執行個體清單

2.注冊中心傳回 6 台服務執行個體,其中 revision 為 1 的服務執行個體為 Instance 1 到 3, revision 為 2 的服務執行個體是 Instance 4 和 Instance 5,revision 為 3 的服務執行個體僅有 Instance 6

3.Consumer 在這 6 台服務執行個體中随機選擇一台,如圖中 Instance 3

4.Consumer 向 Instance 3 發起 MetadataService 的遠端調用,獲得 Dubbo URL 清單,并建立 revision 為 1 的 URL 清單緩存,用 cache = { 1:urls(r1) } 表示

5.(重複步驟 4)Consumer 再從剩餘的 5 台服務執行個體中随機選擇一台,如圖中的 Instance 5,由于 Instance 5 與 Instance 3 的 revision 分為為 2 和 1,此時緩存 cache = { 1:urls(r1) } 未命中,是以 Consumer 将再次發起遠端調用,擷取新的 Dubbo URL 清單,并更新緩存,即 cache = { 1:urls(r1) , 2:urls(r2) }

6.(重複步驟 4)Consumer 再從剩餘的 4 台服務執行個體中随機選擇一台,假設服務執行個體是 Instance 6,由于此時 revision 為3,是以緩存 cache = { 1:urls(r1) , 2:urls(r2) } 再次未命中,再次發起遠端調用,并更新緩存 cache = { 1:urls(r1) , 2:urls(r2) , 3:urls(r3) }

7.(重複步驟 4)由于緩存 cache = { 1:urls(r1) , 2:urls(r2) , 3:urls(r3) } 已覆寫三個 revision 場景,如果該步驟選擇服務執行個體落在 revision 為 1 的子集中,隻需克隆 urls(r1),并根據具體服務執行個體替換部分 host 和 port 等少量元資訊即可,組成成新的 Dubbo URL 清單,依次類推,計算直到剩餘服務執行個體為 0。

大多數情況,revision 的數量不會超過 2,換言之,Consumer 發起 MetadataService 的遠端調用不會超過 2次。無論 revision 數量的大小,架構能夠保證擷取 Dubbo 元資訊的正确性。

當然 MetadataService 并非僅支援 Dubbo URL 中繼資料,還有其他類型的支援。

中繼資料類型

架構上,中繼資料服務(MetadataService)未來将逐漸替代 Dubbo 2.7.0 中繼資料中心,并随着 Dubbo 版本的更疊,所支援的中繼資料類型也将有所變化,比如 Dubbo 2.7.5 中繼資料服務支援的類型包括:

Dubbo 暴露的服務 URL 清單

Dubbo 訂閱的服務 URL 清單

Dubbo 服務定義

關鍵詞:服務自省架構,Apache Dubbo,雲原生,微服務,Spring Cloud