實踐案例:同程藝龍網的 Dubbo 升級經驗總結

ApacheDubbo 發表於 2022-12-02
Dubbo

本篇為同程藝龍旅行網 Apache Dubbo 的實踐案例總結。感興趣的朋友可以訪問官網瞭解更多詳情,或搜尋關注官方微信公眾號 Apache Dubbo 跟進最新動態。

作者資訊:

  • 嚴浩:同程藝龍高階開發,負責服務治理相關工作, Apache Dubbo Committer。
  • 胥皓:同程藝龍高階開發,負責服務治理相關工作。

Dubbo3在同程旅行的實踐

背景

在微服務發展初期,市場上還沒有成熟和流行的 RPC 框架,我們公司內部自研開發了一套名為 DSF (Distributed Service Framework) 的 RPC 框架,支撐起了公司業務的高速發展。但是隨著技術的快速迭代和人員的不斷變更,開發者既要修復之前的 BUG 又要跟上技術的更新,開發維護成本越來越高。另一方面,現在應用程式都在往雲原生方向發展與設計,公司也在這方面做出探索。因此公司微服務框架的演進已經到了岔路口,是全新升級原有的 SDK,還是選擇擁抱開源?

考慮到升級現有的 SDK 在一段時間之後可能依然會面臨現在的問題,最後我們選擇了擁抱開源。在一番調研之後我們選擇了 Dubbo3 作為公司的下一代 RPC 框架,擔任微服務治理體系的資料面。

目前 Dubbo3 在公司的落地開發工作已經完成,透過本文我們對公司內部 Dubbo3 的實踐及收益做了深入總結。

Dubbo3 核心功能介紹

Dubbo 社群關於 Dubbo 3 的文件和資料越來越完善,以下是我們從社群引用的一些內容。

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

Dubbo3 被社群寄予厚望,將其視為下一代雲原生服務框架打造,Dubbo3 提供的核心特性列表,主要包括四部分。

  1. 全新服務發現模型。應用粒度服務發現,面向雲原生設計,適配基礎設施與異構系統;效能與叢集伸縮性大幅提升。
  2. 下一代 RPC 協議 Triple。基於 HTTP/2 的 Triple 協議,相容 gRPC;閘道器穿透性強、多語言友好、支援 Reactive Stream。
  3. 統一流量治理模型。面向雲原生流量治理,SDK、Mesh、VM、Container 等統一治理規則;能夠支援更豐富的流量治理場景。
  4. Service Mesh。在最新的3.1.0的版本中支援Sidecar Mesh 與 Proxyless Mesh,提供更多架構選擇,降低遷移、落地成本。

Dubbo3 的核心功能點(如應用級服務發現以 ip、port 為區分例項)和公司內部的服務模型一致,極大地減少了我們的適配工作。還有在 Service Mesh 中對 Sidecar Mesh 與 Proxyless Mesh 的支援也將減少後續公司對 Mesh 方案的探索成本,包括 Dubbo3 在多語言體系的發展也為異構架構提供了支撐。

總的來說,Dubbo3 非常契合公司的技術體系和後續的發展方向。此外,Dubbo 在開發者中的熟悉度、社群的高活躍度和完善的文件建設也都能為推動 Dubbo3 的使用帶來不少的幫助。

方案調研

在瞭解了 Dubbo3 的核心功能和基本工作原理之後我們開始前期工作階段。

公司內部存在微服務體系 RPC 框架 DSF 和承擔服務發現、路由、上下負載等功能的控制中心,如果讓使用者直接切換到 Dubbo3 使用完全隔離的一套微服務體系會對使用者帶來高額的升級和切換成本。所以我們選擇用 Dubbo3 替換之前的 DSF 框架作為資料面,將 Dubbo3 接入當前的微服務控制中心。同時要求 Dubbo3 支援原有 DSF 框架的私有協議,與 DSF 框架能夠相互發現和呼叫,進一步降低使用者升級成本。

這樣使用者在程式設計習慣上和 Dubbo3 的使用完全保持一致,在服務治理上(如上下負載、同中心路由、例項標籤等功能)的使用與 DSF 保持一致。由於協議相容,新的 Dubbo3 應用和原有 DSF 應用之間也能實現互相發現和呼叫。

要完成這個目標,需要去擴充 Dubbo3 SDK 的註冊模組支援從現有的控制中心進行服務註冊與發現、擴充套件自定義協議與 DSF 服務相互呼叫。藉助於Dubbo 強大的外掛機制,我們在沒有修改 Dubbo 框架任何程式碼的基礎上輕鬆地完成了這個目標,使用者只需要引入 Dubbo 3.0 以上版本的 SDK 和我們開發的外掛包即可。

整體的架構流程如下:

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

Dubbo3 落地的方案需要滿足以下三點要求:

  1. Dubbo3 要接入現有的控制中心,由控制中心完成服務註冊發現和服務治理功能;
  2. Dubbo3 能夠和 DSF 能夠相互呼叫,滿足此要求需要兩個框架能夠互相服務發現並且協議能夠相容;
  3. 透過外掛機制完成所有功能,不能修改 Dubbo 原始碼,使用者可以自由地升級 Dubbo3 SDK 的版本;

服務註冊發現相容

既然需要將 Dubbo3 的應用級註冊接入到控制中心,而且需要與 DSF 服務進行服務發現,就需要了解 Dubbo3 應用級發現的流程才能對其進行更好的擴充。

應用級服務發現核心原理

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

我們從 Dubbo 最經典的工作原理圖說起。Dubbo 從設計之初就內建了服務地址發現的能力,Provider 註冊地址到註冊中心,Consumer 透過訂閱實時獲取註冊中心的地址更新,在收到地址列表後,Consumer 基於特定的負載均衡策略發起對 Provider 的 RPC 呼叫。
在這個過程中:

  1. 每個 Provider 透過特定的 key 向註冊中心註冊本機可訪問地址;
  2. 註冊中心透過這個 key 對 Provider 例項地址進行聚合;
  3. Consumer 透過同樣的 key 從註冊中心訂閱,以便及時收到聚合後的地址列表;

可以看到介面級服務發現是以介面為維度進行服務註冊的,並在註冊資料上攜帶了服務的配置和後設資料。這種方式簡單易用而且可以輕鬆實現應用、介面、方法粒度的服務治理。但由此帶來的註冊資料的放大問題會給註冊中心造成較大壓力,還有就是與現在雲原生的服務模型並不相容,不能與 Kubernetes 相容。

面對這些不足,在 Dubbo3 架構下社群認真思考了兩個問題:

  1. 如何在保留易用性、功能性的同時,重新組織 URL 地址資料,避免冗餘資料的出現,讓 Dubbo 3 能支撐更大規模叢集水平擴容?
  2. 如何在地址發現層面與其他的微服務體系如 Kubernetes、Spring Cloud 打通?

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

最終,社群給出的方案也是非常巧妙和經典,將之前介面級服務的資料拆成兩部分。屬於例項模型的 ip 和 port 註冊到註冊中心,而屬於業務屬性的 RPC 後設資料和 RPC 服務配置統一由 Dubbo Provider 的 MetadataService RPC 服務提供或者由後設資料中心提供。在服務消費端和提供端之間建立了一條內建的 RPC 服務資訊協商機制,也稱為"服務自省"。全新的應用級服務發現模型,相比之前介面級別單機記憶體下降 50% 且極大的減少了註冊中心的壓力。

還有一個問題是換成了應用級服務發現之後,Consumer 是如何知道訂閱的介面是屬於哪一個服務的?因為 Dubbo 的程式設計模型是以介面為維度的。Dubbo3 提供了兩種解決方案:一是從後設資料中心儲存介面和應用名的對映關係,二是透過 Consumer 在介面上透過 providerBy 配置手動指定服務提供方的名稱。

相容方案

如果是 Dubbo Consumer 呼叫 Dubbo Provider,我們只需要按部就班參考其他應用級別服務發現的外掛比如 Zookeeper、Nacos 開發就可以完成此功能。如果 DSF Client 要呼叫 Dubbo Provider 我們是將相容邏輯放在了控制中心,避免使用者 SDK 的升級成本。剩下的相容流程只有 Dubbo Consumer 呼叫 DSF Server 了,因為要求儘量不要修改 Dubbo 框架的原始碼,所以這裡的相容邏輯我們只能在註冊中心的外掛中完成。

上面介紹應用級服務發現的核心原理的時候提到應用級服務發現有 3 個關鍵的步驟

  1. 透過後設資料的 mapping 獲取介面對應的服務名或者透過介面配置中的 providerBy 指定;
  2. 透過服務名獲取例項列表,例項以 ip、port 為維度,並且在例項資訊中攜帶後設資料的 revision;
  3. 呼叫 Dubbo Provider 的 MetaService 獲取例項的後設資料資訊,組裝介面資料;

Dubbo 服務 呼叫 DSF 服務相容流程的第一步非常簡單,因為 DSF 並沒有介面與服務名的 mapping 資料,所以透過 providerBy 指定介面所屬的 DSF 服務名。第二步因為 DSF 服務的註冊模型也是應用級的,例項的資料完全可以相容這一部分也很簡單。最關鍵的一步在於如何獲取 DSF 服務的後設資料,很顯然現有的 DSF 服務並不具有 MetaService 的介面。

上面提到 Dubbo3 支援兩種方式獲取例項的後設資料。預設就是從 Dubbo Provider 的 MetaService 獲取例項的後設資料資訊,也支援從後設資料中心獲取例項的後設資料資訊,只需要將例項的 dubbo.metadata.storage-type 屬性設定為 remote 即可。而 DSF 服務正好釋出了 API 的契約資料到控制中心用作服務測試和定址相容,完全可以將 DSF 的契約資料轉換為 Dubbo 的後設資料的格式,滿足服務發現的流程。

以下為 Dubbo Consumer 發現 DSF 服務的流程

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

完成服務發現的相容之後,使用者在呼叫 DSF 服務的時候僅需要在介面上透過 providerBy 指定介面對應的服務即可,使用成本極低。

協議相容與服務治理

協議相容

完成服務發現的相互相容之後,離 Dubbo 與 DSF 服務的相互呼叫的目標只剩最後一塊拼圖,在外掛中實現 DSF 協議即可。
相比服務發現的各種資料相容,協議的相容比較清晰,只需要根據 Dubbo 協議擴充套件說明進行自定義協議擴充套件完成 DSF 資料格式相容即可。

Dubbo 協議擴充套件需要實現以下介面:

  • org.apache.dubbo.rpc.Exporter
  • org.apache.dubbo.rpc.Invoker
  • org.apache.dubbo.rpc.Protocol

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

當使用者呼叫 refer() 所返回的 Invoker 物件的 invoke() 方法時,協議需相應執行同 URL 遠端 export() 傳入的 Invoker 物件的 invoke() 方法。其中,refer() 返回的 Invoker 由協議實現,協議通常需要在此 Invoker 中傳送遠端請求,export() 傳入的 Invoker 由框架實現並傳入,協議不需要關心。也就是說服務提供方在容器啟動的時候就進行服務的暴露,而服務呼叫方需要透過協議進行Invoker的呼叫。我們的擴充套件如下:

實踐案例:同程藝龍網的 Dubbo 升級經驗總結

最後完成的效果如下,使用者只需要在 pom 中引入 Dubbo3 以上的任意版本和開發的外掛,配置上指定註冊中心地址和協議為 dsf 即可,其他使用方式和 Dubbo3 保持一致。

<properties>
    <dubbo.version>3.0.11</dubbo.version>
    <dubbo-dsf.version>1.0.0</dubbo-dsf.version>
</properties>

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>${dubbo.version}</version>
</dependency>
<dependency>
    <groupId>com.ly.dsf</groupId>
    <artifactId>dubbo-dsf-extensions-all</artifactId>
    <version>${dubbo-dsf.version}</version>
</dependency>

配置檔案

# 註冊地址為控制中心
dubbo.registry.address=dsf://{address}
# 協議指定 dsf
dubbo.protocol.name=dsf
dubbo.consumer.protocol=dsf

服務治理

Dubbo3 有非常強大的流量治理的功能,同時我們內部的控制中心也有服務治理的功能,部分功能也有重合。

對於這部分的取捨,我們打算控制中心原有的功能對 Dubbo3 服務依然支援,如服務發現、同中心定址、上下負載、服務測試等操作和之前保持一致。而 Dubbo 特有的服務治理功能如動態配置、Mesh 路由,我們將新增功能對其支援,保證 Dubbo3 功能的完整。

總結

Dubbo 3 是一個優秀的微服務框架,提供的 SPI 以及 Extension 機制能夠非常方便的讓使用者去擴充套件實現想要功能。而且 Dubbo3 也更適應目前雲原生的架構,Dubbo 3.1.x 版本支援 Sidecar 和 Proxyless 的 Mesh 方案,而且社群也在準備開源 Java Agent 方式的 Proxyless,這樣就能較好的將微服務架框的 Framework 與資料面解耦,降低微服務框架的維護成本和升級成本。我們也會和社群一起探索,共建 Dubbo 社群的繁榮。

搜尋關注官方微信公眾號:Apache Dubbo,瞭解更多業界最新動態,掌握大廠面試必備 Dubbo 技能