里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

Kirito的部落格發表於2022-12-05
作者簡介:劉軍,GitHub賬號Chickenlj,Apache Dubbo PMC,專案核心維護者,見證了Dubbo從重啟開源到Apache畢業的整個流程。現任職阿里云云原生應用平臺團隊,參與服務框架、微服務相關工作,目前主要在推動Dubbo開源的雲原生化。


近日,備受矚目的 Apache Dubbo(以下簡稱 Dubbo)2.7.5 版本正式釋出,在 2.7.5 版本中,Dubbo 引入了很多新的特性、對現有的很多功能做了增強、同時在效能上也有了非常大的提升,這個版本無論對 Dubbo 社群亦或是開發者來說,都將是一個里程碑式的版本。


  • 應用粒度服務註冊【beta】

  • HTTP/2 (gRPC) 協議支援

  • Protobuf 支援

  • 效能最佳化,呼叫鏈路效能提升 30%

  • 支援 TLS 安全傳輸鏈路

  • 最佳化的消費端執行緒模型

  • 新增更適應多叢集部署場景的負載均衡策略

  • 全新的應用開發 API (相容老版本應用)【beta】

  • 其他一些功能增強與 bugfix

首先,從服務發現上,新版本突破以往基於介面粒度的模型,引入了全新的基於應用粒度的服務發現機制 - 服務自省,雖然該機制當前仍處於 beta 階段,但對於 Dubbo 向整個微服務雲原生體系靠齊,都打下了非常好的基礎;得益於緊湊的協議設計和程式碼實現上的最佳化,Dubbo 一直以來都具有較好的效能表現,在 2.7.5 版本中,效能上有了進一步的提升,根據來自官方維護團隊的壓測,新版本在呼叫鏈路上效能提升達到 30%;雲原生微服務時代,多語言需求變得越來越普遍,協議的通用性和穿透性對於構建打通前後端的整套微服務體系也變得非常關鍵,Dubbo 透過實現 gRPC 協議實現了對 HTTP/2 協議的支援,同時增加了與 Protobuf 的結合。

1. 應用粒度服務註冊【beta】

從 Java 實現版本的角度來說,Dubbo 是一個面向介面代理的服務開發框架,服務定義、服務釋出以及服務引用都是基於介面,服務治理層面包括服務發現、各種規則定義也都是基於介面定義的,基於介面可以說是 Dubbo 的一大優勢,比如向開發者遮蔽了遠端呼叫細節、治理粒度更精細等。但基於介面的服務定義同時也存在一些問題,如服務,與業界通用的微服務體系等。

里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

針對以上問題,2.7.5 版本引入了一種新的服務定義/治理機制:服務自省,簡單來說這是一種基於應用粒度的服務治理方案。一個例項只向註冊中心註冊一條記錄,徹底解決服務推送效能瓶頸,同時由於這樣的模型與主流微服務體系如 SpringCloud、K8S 等天然是對等的,因此為 Dubbo 解決和此類異構體系間的互聯互通清除了障礙。有興趣進一步瞭解 Dubbo 服務自省機制如何解決異構微服務體系互聯互通問題的,可具體參考我們之前的文章解析《Dubbo 如何成為聯通異構微服務體系的最佳服務開發框架》。

以下是服務自省機制的基本工作原理圖。

里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

要了解更多關於服務自省工作原理的細節,請參與官方文件及後續文章。

服務自省與當前已有的機制之間可以說是互補的關係,Dubbo 框架會繼續保持介面粒度的服務治理的優勢,實現介面和應用兩個粒度互為補充的局面,兼顧效能、靈活性和通用性,力爭使 Dubbo 成為微服務開發的最佳框架。

2. HTTP/2 (gRPC) 協議支援

Dubbo RPC 協議是構建在 TCP 之上,這有很多優勢也有一些缺點,缺點比如通用性、協議穿透性不強,對多語言實現不夠友好等。HTTP/2 由於其標準 HTTP 協議的屬性,無疑將具有更好的通用性,現在或將來在各層網路裝置上肯定都會得到很好的支援,gRPC 之所以選在 HTTP/2 作為傳輸層載體很大程度上也是因為這個因素。當前 gRPC 在雲原生、Mesh 等體系下的認可度和採用度逐步提升,儼然有成為 RPC 協議傳輸標準的趨勢,Dubbo 和 gRPC 在協議層面是對等競爭的,但是在框架實現上卻各有側重,Dubbo 無疑有更豐富的服務開發和治理體驗 。

Dubbo 支援 gRPC 協議帶來的直觀好處有:

  • 正式支援基於 HTTP/2 的遠端通訊,在協議通用性和穿透性上進一步提升。

  • 支援跨程式的 Stream 流式通訊,支援 Reactive 風格的 RPC 程式設計。

  • 解決了 gRPC 框架難以直接用於微服務開發的問題,將其納入 Dubbo 的服務治理體系。

  • 為聯通組織內部已有的 gRPC 或多語言體系提供支援。

2.7.5 版本開始,gRPC (HTTP/2) 成為 Dubbo 協議體系中的一等公民,對於有需求的開發者完全可以在 Dubbo 開發的微服務體系中啟用 gRPC 協議,而不必束縛在 Dubbo 協議自身上,關於這點我們在《Dubbo 如何成為聯通異構微服務體系的最佳服務開發框架》一文中也有類似的觀點表述。

關於 Dubbo 中如何開發 grpc (HTTP/2) 服務的細節,請參考文章《Dubbo 在跨語言與協議穿透性等方面的探索》,關於如何開啟 TLS 和使用 Reactive RPC 程式設計,請參見示例 

及  另外,Dubbo 的 go 版本目前同樣也提供了對 gRPC 協議對等的支援,具體請關注 dubbogo 社群的發版計劃。

3. Protobuf 支援

支援 Protobuf 更多的是從解決 Dubbo 跨語言易用性的角度考慮的。

跨語言的服務開發涉及到多個方面,從服務定義、RPC 協議到序列化協議都要做到語言中立,同時還針對每種語言有對應的 SDK 實現。雖然得益於社群的貢獻,現在 Dubbo 在多語言 SDK 實現上逐步有了起色,已經提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客戶端或全量實現版本,但在以上提到的跨語言友好性方面,以上三點還是有很多可改進之處。

協議上 2.7.5 版本支援了 gRPC,而關於服務定義與序列化,Protobuf 則提供了很好的解決方案。

  • 服務定義。當前 Dubbo 的服務定義和具體的程式語言繫結,沒有提供一種語言中立的服務描述格式,比如 Java 就是定義 Interface 介面,到了其他語言又得重新以另外的格式定義一遍。因此 Dubbo 透過支援 Protobuf 實現了語言中立的服務定義。

  • 序列化。Dubbo 當前支援的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而這其中支援跨語言的只有 Json、Hessian2,通用的 Json 有固有的效能問題,而 Hessian2 無論在效率還是多語言 SDK 方面都有所欠缺。為此,Dubbo 透過支援 Protobuf 序列化來提供更高效、易用的跨語言序列化方案。

日後,不論我們使用什麼語言版本來開發 Dubbo 服務,都可以直接使用 IDL 定義如下服務,具體請參見示例

syntax = "proto3";
option java_multiple_files = true;option java_package = "org.apache.dubbo.demo";option java_outer_classname = "DemoServiceProto";option objc_class_prefix = "DEMOSRV";
package demoservice;
// The demo service definition.service DemoService {  rpc SayHello (HelloRequest) returns (HelloReply) {}}
// The request message containing the user's name.message HelloRequest {  string name = 1;}
// The response message containing the greetingsmessage HelloReply {  string message = 1;}

4. 效能最佳化

4.1 呼叫鏈路最佳化

2.7.5 版本對整個呼叫鏈路做了全面的最佳化,根據壓測結果顯示,總體 QPS 效能提升將近 30%,同時也減少了呼叫過程中的記憶體分配開銷。其中一個值得提及的設計點是 2.7.5 引入了 Servicerepository 的概念,在服務註冊階段提前生成 ServiceDescriptor 和 MethodDescriptor,以減少 RPC 呼叫階段計算 Service 原資訊帶來的資源消耗。

4.2 消費端執行緒池模型最佳化

對 2.7.5 版本之前的 Dubbo 應用,尤其是一些消費端應用,當面臨需要消費大量服務且併發數比較大的大流量場景時(典型如閘道器類場景),經常會出現消費端執行緒數分配過多的問題,具體問題討論可參見以下 issue :

改進後的消費端執行緒池模型,透過複用業務端被阻塞的執行緒,很好的解決了這個問題。

老的執行緒池模型

里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

我們重點關注 Consumer 部分:


  1. 業務執行緒發出請求,拿到一個 Future 例項。

  2. 業務執行緒緊接著呼叫 future.get 阻塞等待業務結果返回。

  3. 當業務資料返回後,交由獨立的 Consumer 端執行緒池進行反序列化等處理,並呼叫 future.set 將反序列化後的業務結果置回。

  4. 業務執行緒拿到結果直接返回

2.7.5 版本引入的執行緒池模型

里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

  1. 業務執行緒發出請求,拿到一個 Future 例項。

  2. 在呼叫 future.get() 之前,先呼叫 ThreadlessExecutor.wait(),wait 會使業務執行緒在一個阻塞佇列上等待,直到佇列中被加入元素。

  3. 當業務資料返回後,生成一個 Runnable Task 並放入 ThreadlessExecutor 佇列

  4. 業務執行緒將 Task 取出並在本執行緒中執行:反序列化業務資料並 set 到 Future。

  5. 業務執行緒拿到結果直接返回

這樣,相比於老的執行緒池模型,由業務執行緒自己負責監測並解析返回結果,免去了額外的消費端執行緒池開銷。

關於效能最佳化,在接下來的版本中將會持續推進,主要從以下兩個方面入手:

  1. RPC 呼叫鏈路。目前能看到的點包括:進一步減少執行鏈路的記憶體分配、在保證協議相容性的前提下提高協議傳輸效率、提高 Filter、Router 等計算效率。

  2. 服務治理鏈路。進一步減少地址推送、服務治理規則推送等造成的記憶體、cpu 資源消耗。

5. TLS 安全傳輸鏈路

2.7.5 版本在傳輸鏈路的安全性上做了很多工作,對於內建的 Dubbo Netty Server 和新引入的 gRPC 協議都提供了基於 TLS 的安全鏈路傳輸機制。


TLS 的配置都有統一的入口,如下所示:

Provider 端

SslConfig sslConfig = new SslConfig();
sslConfig.setServerKeyCertChainPath("path to cert");
sslConfig.setServerPrivateKeyPath(args[1]);
// 如果開啟雙向 cert 認證
if (mutualTls) {
  sslConfig.setServerTrustCertCollectionPath(args[2]);
}

ProtocolConfig protocolConfig = new ProtocolConfig("dubbo/grpc");
protocolConfig.setSslEnabled(true);

Consumer 端

if (!mutualTls) {}
    sslConfig.setClientTrustCertCollectionPath(args[0]);
} else {
    sslConfig.setClientTrustCertCollectionPath(args[0]);
    sslConfig.setClientKeyCertChainPath(args[1]);
    sslConfig.setClientPrivateKeyPath(args[2]);
}

為儘可能保證應用啟動的靈活性,TLS Cert 的指定還能透過 -D 引數或環境變數等方式來在啟動階段根據部署環境動態指定,具體請參見 Dubbo 配置讀取規則與 TLS 示例

Dubbo 配置讀取規則:

TLS 示例:

如果要使用的是 gRPC 協議,在開啟 TLS 時會使用到協議協商機制,因此必須使用支援 ALPN 機制的 Provider,推薦使用的是 netty-tcnative,具體可參見 gRPC Java 社群的總結:

在服務呼叫的安全性上,Dubbo 在後續的版本中會持續投入,其中服務發現/呼叫的鑑權機制預計在接下來的版本中就會和大家見面。

6. Bootstrap API【beta】

在上面講《服務自省》時,我們提到了 Dubbo 面向介面的設計,面向介面程式設計、面向介面做服務發現和服務治理。在引入應用粒度服務發現的同時,2.7.5 版本對程式設計入口也做了最佳化,在相容老版本 API 的同時,新增了新的面向應用的程式設計介面 - DubboBootstrap。


以面向 Dubbo API 程式設計為例,以前我們要這麼寫:

ServiceConfig<GreetingsService> service1 = new ServiceConfig<>();
service1.setApplication(new ApplicationConfig("first-dubbo-provider"));
service1.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
service1.export();

ServiceConfig<GreetingsService> service2 = new ServiceConfig<>();
service2.setApplication(new ApplicationConfig("first-dubbo-provider"));
service2.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":2181"));
service2.export();

......

ApplicationConfig、RegistryConfig、ProtocolConfig 等全域性性的配置要在每個服務上去配置;並且從 Dubbo 框架的角度,由於缺少一個統一的 Server 入口,一些例項級別的配置如 ShutdownHook、ApplicationListener、應用級服務治理元件等都缺少一個載入驅動點。

在引入 DubboBootstrap 後,新的程式設計模型變得更簡單,並且也為解決了缺少例項級啟動入口的問題

ProtocolConfig protocolConfig = new ProtocolConfig("grpc");
protocolConfig.setSslEnabled(true);

SslConfig sslConfig = new SslConfig();
sslConfig.setXxxCert(...);

DubboBootstrap bootstrap = DubboBootstrap.getInstance();
bootstrap.application(new ApplicationConfig("ssl-provider"))
  .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
  .protocol(protocolConfig)
  .ssl(sslConfig);

ServiceConfig<GreetingsService> service1 = new ServiceConfig<>();
ServiceConfig<GreetingsService> service2 = new ServiceConfig<>();

bootstrap.service(service1).service(service2);
bootstrap.start();

7. 多註冊中心叢集負載均衡

對於多註冊中心訂閱的場景,選址時的多了一層註冊中心叢集間的負載均衡:

里程碑式 Dubbo 2.7.5 版本釋出,效能提升30%,支援 HTTP/2、TLS、Protobuf等特性

在 Cluster Invoker 這一級,我們支援的選址策略有(2.7.5+ 版本,具體使用請參見文件):

指定優先順序

<!-- 來自 preferred=“true” 註冊中心的地址將被優先選擇,只有該中心無可用地址時才 Fallback 到其他註冊中心 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />

同 zone 優先

<!-- 選址時會和流量中的 zone key 做匹配,流量會優先派發到相同 zone 的地址 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />

權重輪詢

<!-- 來自北京和上海叢集的地址,將以 10:1 的比例來分配流量 -->
<dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight=”100“ />
<dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight=”10“ />
  • 預設,stick to 任意可用

關於多註冊中心訂閱模型,Dubbo 同時也提供了 Multi-Registry 合併的解決思路,歡迎參與到以下 PR 的討論中:

8. 其他功能增強

  • 新增地址變更事件通知介面,方便業務側感知地址變化

  • 新增外圍配置載入入口,方便開發者在啟動階段定製服務啟動引數

  • config 模組重構

  • parameters 擴充套件配置增強

  • 其他一些 Bugfix

從 Dubbo 框架自身的角度來說,2.7.5 版本也做了很多的重構與最佳化(比如說 config 模組的重構),這些改動對於使用者來說並無感知的,但是從最佳化整個 Dubbo 程式碼內部結構的角度來說,這些改動對後續的功能開發與新機制的引入是一個很好的鋪墊。

9. 總結與展望

在後續的版本中,Dubbo 會持續快速的最佳化與迭代,主要從以下幾個方面發力:

  • 繼續探索服務自省成為 Dubbo 主推的服務治理模型。

  • 對於企業使用者關心的微服務解決方案場景,會持續推進框架的演進,包括當前正在開發的配置、服務鑑權機制、熔斷等功能。後續還會嘗試聯合社群推動周邊配套設施如閘道器、治理平臺 Admin 等的建設,非常期待社群能踴躍參與到此部分的建設中。

  • 效能最佳化上。主要從兩個方面著手,一是呼叫鏈路的持續最佳化,同時繼續探索新的更通用的 RPC 協議;另一方面是在服務治理推送機制上的最佳化,以進一步提高 Dubbo 在大規模服務地址推送場景下的表現。

  • 雲原生方向。接下來的版本將重點探索,1. 如何更好的支援 Dubbo 在 Kubernetes 上的部署和服務治理;2. 對於混合部署的場景,如傳統 VM 和 K8S 體系混合部署、SDK Dubbo 與 Mesh 混合部署的場景,如何提供更好的支援以實現混部場景的長期共存或遷移。

本文由高可用架構約稿。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556476/viewspace-2672528/,如需轉載,請註明出處,否則將追究法律責任。

相關文章