什麼是緩存? 為什麼使用緩存? 什麼場景下使用緩存? 緩存(Cache)就是數據交換的緩衝區,一個臨時存儲數據的地方,當我們讀取數據時會首先從緩存中查找需要的數據,如果找到了則直接執行,找不到的話再從記憶體中找。 在實際開發中,我們會經常對資料庫進行數據查詢,而從資料庫讀取數據的效率是非常低下的,並且 ...
本篇為同程藝龍旅行網 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 的文檔和資料越來越完善,以下是我們從社區引用的一些內容。
Dubbo3 被社區寄予厚望,將其視為下一代雲原生服務框架打造,Dubbo3 提供的核心特性列表,主要包括四部分。
- 全新服務發現模型。應用粒度服務發現,面向雲原生設計,適配基礎設施與異構系統;性能與集群伸縮性大幅提升。
- 下一代 RPC 協議 Triple。基於 HTTP/2 的 Triple 協議,相容 gRPC;網關穿透性強、多語言友好、支持 Reactive Stream。
- 統一流量治理模型。面向雲原生流量治理,SDK、Mesh、VM、Container 等統一治理規則;能夠支持更豐富的流量治理場景。
- 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 和我們開發的插件包即可。
整體的架構流程如下:
Dubbo3 落地的方案需要滿足以下三點要求:
- Dubbo3 要接入現有的控制中心,由控制中心完成服務註冊發現和服務治理功能;
- Dubbo3 能夠和 DSF 能夠相互調用,滿足此要求需要兩個框架能夠互相服務發現並且協議能夠相容;
- 通過插件機制完成所有功能,不能修改 Dubbo 源碼,用戶可以自由地升級 Dubbo3 SDK 的版本;
服務註冊發現相容
既然需要將 Dubbo3 的應用級註冊接入到控制中心,而且需要與 DSF 服務進行服務發現,就需要瞭解 Dubbo3 應用級發現的流程才能對其進行更好的拓展。
應用級服務發現核心原理
我們從 Dubbo 最經典的工作原理圖說起。Dubbo 從設計之初就內置了服務地址發現的能力,Provider 註冊地址到註冊中心,Consumer 通過訂閱實時獲取註冊中心的地址更新,在收到地址列表後,Consumer 基於特定的負載均衡策略發起對 Provider 的 RPC 調用。
在這個過程中:
- 每個 Provider 通過特定的 key 向註冊中心註冊本機可訪問地址;
- 註冊中心通過這個 key 對 Provider 實例地址進行聚合;
- Consumer 通過同樣的 key 從註冊中心訂閱,以便及時收到聚合後的地址列表;
可以看到介面級服務發現是以介面為維度進行服務註冊的,併在註冊數據上攜帶了服務的配置和元數據。這種方式簡單易用而且可以輕鬆實現應用、介面、方法粒度的服務治理。但由此帶來的註冊數據的放大問題會給註冊中心造成較大壓力,還有就是與現在雲原生的服務模型並不相容,不能與 Kubernetes 相容。
面對這些不足,在 Dubbo3 架構下社區認真思考了兩個問題:
- 如何在保留易用性、功能性的同時,重新組織 URL 地址數據,避免冗餘數據的出現,讓 Dubbo 3 能支撐更大規模集群水平擴容?
- 如何在地址發現層面與其他的微服務體系如 Kubernetes、Spring Cloud 打通?
最終,社區給出的方案也是非常巧妙和經典,將之前介面級服務的數據拆成兩部分。屬於實例模型的 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 個關鍵的步驟
- 通過元數據的 mapping 獲取介面對應的服務名或者通過介面配置中的 providerBy 指定;
- 通過服務名獲取實例列表,實例以 ip、port 為維度,並且在實例信息中攜帶元數據的 revision;
- 調用 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 服務的流程
完成服務發現的相容之後,用戶在調用 DSF 服務的時候僅需要在介面上通過 providerBy 指定介面對應的服務即可,使用成本極低。
協議相容與服務治理
協議相容
完成服務發現的相互相容之後,離 Dubbo 與 DSF 服務的相互調用的目標只剩最後一塊拼圖,在插件中實現 DSF 協議即可。
相比服務發現的各種數據相容,協議的相容比較清晰,只需要根據 Dubbo 協議擴展說明進行自定義協議擴展完成 DSF 數據格式相容即可。
Dubbo 協議擴展需要實現以下介面:
org.apache.dubbo.rpc.Exporter
org.apache.dubbo.rpc.Invoker
org.apache.dubbo.rpc.Protocol
當用戶調用 refer()
所返回的 Invoker
對象的 invoke()
方法時,協議需相應執行同 URL 遠端 export()
傳入的 Invoker
對象的 invoke()
方法。其中,refer()
返回的 Invoker
由協議實現,協議通常需要在此 Invoker
中發送遠程請求,export()
傳入的 Invoker
由框架實現並傳入,協議不需要關心。也就是說服務提供方在容器啟動的時候就進行服務的暴露,而服務調用方需要通過協議進行Invoker的調用。我們的擴展如下:
最後完成的效果如下,用戶只需要在 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 技能