Dubbo2.7 三大新特性詳解

自 2017 年 7 月阿里重啓 Dubbo 開源,到目前爲止 github star 數,contributor 數都有了非常大的提升。2018 年 2 月 9 日阿里決定將 Dubbo 項目貢獻給 Apache,經過一週的投票,順利成爲了 Apache 的孵化項目,也就是大家現在看到的 Incubator Dubbo。預計在 2019 年 4 月,Dubbo 可以達成畢業,成爲 Apache 的頂級項目。

分支介紹

Dubbo 目前有如圖所示的 5 個分支,其中 2.7.1-release 只是一個臨時分支,忽略不計,對其他 4 個分支進行介紹。

  • 2.5.x 近期已經通過投票,Dubbo 社區即將停止對其的維護。
  • 2.6.x 爲長期支持的版本,也是 Dubbo 貢獻給 Apache 之前的版本,其包名前綴爲:com.alibaba,JDK 版本對應 1.6。
  • 3.x-dev 是前瞻性的版本,對 Dubbo 進行一些高級特性的補充,如支持 rx 特性。
  • master 爲長期支持的版本,版本號爲 2.7.x,也是 Dubbo 貢獻給 Apache 的開發版本,其包名前綴爲:org.apache,JDK 版本對應 1.8。

如果想要研究 Dubbo 的源碼,建議直接瀏覽 master 分支。

Dubbo 2.7 新特性

Dubbo 2.7.x 作爲 Apache 的孵化版本,除了代碼優化之外,還新增了許多重磅的新特性,本文將會介紹其中最典型的三個新特性:

  • 異步化改造
  • 三大中心改造
  • 服務治理增強

異步化改造

幾種調用方式

在遠程方法調用中,大致可以分爲這 4 種調用方式。oneway 指的是客戶端發送消息後,不需要接受響應。對於那些不關心服務端響應的請求,比較適合使用 oneway 通信。

注意,void hello() 方法在遠程方法調用中,不屬於 oneway 調用,雖然 void 方法表達了不關心返回值的語義,但在 RPC 層面,仍然需要做通信層的響應。

sync 是最常用的通信方式,也是默認的通信方法。

future 和 callback 都屬於異步調用的範疇,他們的區別是:在接收響應時,future.get() 會導致線程的阻塞;callback 通常會設置一個回調線程,當接收到響應時,自動執行,不會對當前線程造成阻塞。

Dubbo 2.6 異步化

異步化的優勢在於客戶端不需要啓動多線程即可完成並行調用多個遠程服務,相對多線程開銷較小。介紹 2.7 中的異步化改造之前,先回顧一下如何在 2.6 中使用 Dubbo 異步化的能力。

  1. 將同步接口聲明成 async=true
<dubbo:reference id="asyncService" interface="org.apache.dubbo.demo.api.AsyncService" async="true"/>
public interface AsyncService {
    String sayHello(String name);
}
  1. 通過上下文類獲取 future
AsyncService.sayHello("Han Meimei");
Future<String> fooFuture = RpcContext.getContext().getFuture();
fooFuture.get();

可以看出,這樣的使用方式,不太符合異步編程的習慣,竟然需要從一個上下文類中獲取到 Future。如果同時進行多個異步調用,使用不當很容易造成上下文污染。而且,Future 並不支持 callback 的調用方式。這些弊端在 Dubbo 2.7 中得到了改進。

Dubbo 2.7 異步化

  1. 無需配置中特殊聲明,顯示聲明異步接口即可
public interface AsyncService {
    String sayHello(String name);
    default CompletableFuture<String> sayHiAsync(String name) {
        return CompletableFuture.completedFuture(sayHello(name));
    }
}
  1. 使用 callback 方式處理返回值
CompletableFuture<String> future = asyncService.sayHiAsync("Han MeiMei");
future.whenComplete((retValue, exception) -> {
    if (exception == null) {
        System.out.println(retValue);
    } else {
        exception.printStackTrace();
    }
});

Dubbo 2.7 中使用了 JDK1.8 提供的 CompletableFuture 原生接口對自身的異步化做了改進。 CompletableFuture 可以支持 future 和 callback 兩種調用方式,用戶可以根據自己的喜好和場景選擇使用,非常靈活。

異步化設計 FAQ

Q:如果 RPC 接口只定義了同步接口,有辦法使用異步調用嗎?

A:2.6 中的異步調用唯一的優勢在於,不需要在接口層面做改造,又可以進行異步調用,這種方式仍然在 2.7 中保留;使用 Dubbo 官方提供的 compiler hacker,編譯期自動重寫同步方法,請 在此 討論和跟進具體進展。

Q:關於異步接口的設計問題,爲何不提供編譯插件,根據原接口,自動編譯出一個 XxxAsync 接口?

A:Dubbo 2.7 採用採用過這種設計,但接口的膨脹會導致服務類的增量發佈,而且接口名的變化會影響服務治理的一些相關邏輯,改爲方法添加 Async 後綴相對影響範圍較小。

Q:Dubbo 分爲了客戶端異步和服務端異步,剛剛你介紹的是客戶端異步,爲什麼不提服務端異步呢?

A:Dubbo 2.7 新增了服務端異步的支持,但實際上,Dubbo 的業務線程池模型,本身就可以理解爲異步調用,個人認爲服務端異步的特性較爲雞肋。

三大中心改造

三大中心指的:註冊中心,元數據中心,配置中心。

在 2.7 之前的版本,Dubbo 只配備了註冊中心,主流使用的註冊中心爲 zookeeper。新增加了元數據中心和配置中心,自然是爲了解決對應的痛點,下面我們來詳細闡釋三大中心改造的原因。

元數據改造

元數據是什麼?元數據定義爲描述數據的數據,在服務治理中,例如服務接口名,重試次數,版本號等等都可以理解爲元數據。在 2.7 之前,元數據一股腦丟在了註冊中心之中,這造成了一系列的問題:

推送量大 -> 存儲數據量大 -> 網絡傳輸量大 -> 延遲嚴重

生產者端註冊 30+ 參數,有接近一半是不需要作爲註冊中心進行傳遞;消費者端註冊 25+ 參數,只有個別需要傳遞給註冊中心。有了以上的理論分析,Dubbo 2.7 進行了大刀闊斧的改動,只將真正屬於服務治理的數據發佈到註冊中心之中,大大降低了註冊中心的負荷。

同時,將全量的元數據發佈到另外的組件中:元數據中心。元數據中心目前支持 redis(推薦),zookeeper。這也爲 Dubbo 2.7 全新的 Dubbo Admin 做了準備,關於新版的 Dubbo Admin,我將會後續準備一篇獨立的文章進行介紹。

示例:使用 zookeeper 作爲元數據中心

<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>

Dubbo 2.6 元數據

dubbo://30.5.120.185:20880/com.alibaba.dubbo.demo.DemoService?
anyhost=true&
application=demo-provider&
interface=com.alibaba.dubbo.demo.DemoService&
methods=sayHello&
bean.name=com.alibaba.dubbo.demo.DemoService&
dubbo=2.0.2&
executes=4500&
generic=false&
owner=kirito&
pid=84228&
retries=7&
side=provider&
timestamp=1552965771067

從本地的 zookeeper 中取出一條服務數據,通過解碼之後,可以看出,的確有很多參數是不必要。

Dubbo 2.7 元數據

在 2.7 中,如果不進行額外的配置,zookeeper 中的數據格式仍然會和 Dubbo 2.6 保持一致,這主要是爲了保證兼容性,讓 Dubbo 2.6 的客戶端可以調用 Dubbo 2.7 的服務端。如果整體遷移到 2.7,則可以爲註冊中心開啓簡化配置的參數:

<dubbo:registry address=“zookeeper://127.0.0.1:2181” simplified="true"/>

Dubbo 將會只上傳那些必要的服務治理數據,一個簡化過後的數據如下所示:

dubbo://30.5.120.185:20880/org.apache.dubbo.demo.api.DemoService?
application=demo-provider&
dubbo=2.0.2&
release=2.7.0&
timestamp=1552975501873

對於那些非必要的服務信息,仍然全量存儲在元數據中心之中:

元數據中心的數據可以被用於服務測試,服務 MOCK 等功能。目前註冊中心配置中 simplified 的默認值爲 false,因爲考慮到了遷移的兼容問題,在後續迭代中,默認值將會改爲 true。

配置中心支持

衡量配置中心的必要性往往從三個角度出發:

  1. 分佈式配置統一管理

  2. 動態變更推送

  3. 安全性

Spring Cloud Config, Apollo, Nacos 等分佈式配置中心組件都對上述功能有不同程度的支持。在 2.7 之前的版本中,在 zookeeper 中設置了部分節點:configurators,routers,用於管理部分配置和路由信息,它們可以理解爲 Dubbo 配置中心的雛形。在 2.7 中,Dubbo 正式支持了配置中心,目前支持的幾種註冊中心 Zookeeper,Apollo,Nacos(2.7.1-release 支持)。

在 Dubbo 中,配置中心主要承擔了兩個作用

  • 外部化配置。啓動配置的集中式存儲

  • 服務治理。服務治理規則的存儲與通知

示例:使用 Zookeeper 作爲配置中心

<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>

引入配置中心後,需要注意配置項的覆蓋問題,優先級如圖所示

服務治理增強

我更傾向於將 Dubbo 當做一個服務治理框架,而不僅僅是一個 RPC 框架。在 2.7 中,Dubbo 對其服務治理能力進行了增強,增加了標籤路由的能力,並抽象出了應用路由和服務路由的概念。在最後一個特性介紹中,着重對標籤路由 TagRouter 進行探討。

在服務治理中,路由層和負載均衡層的對比。區別 1,Router:m 選 n,LoadBalance:n 選 1;區別 2,路由往往是疊加使用的,負載均衡只能配置一種。

在很長的一段時間內,Dubbo 社區經常有人提的一個問題是:Dubbo 如何實現流量隔離和灰度發佈,直到 2.7 提供了標籤路由,用戶可以使用這個功能,來實現上述的需求。

標籤路由提供了這樣一個能力,當調用鏈路爲 A -> B -> C -> D 時,用戶給請求打標,最典型的打標方式可以藉助 attachment(他可以在分佈式調用中傳遞下去),調用會優先請求那些匹配的服務端,如 A -> B,C -> D,由於集羣中未部署 C 節點,則會降級到普通節點。

打標方式會收到集成系統差異的影響,從而導致很大的差異,所以 Dubbo 只提供了 RpcContext.getContext().setAttachment() 這樣的基礎接口,用戶可以使用 SPI 擴展,或者 server filter 的擴展,對測試流量進行打標,引導進入隔離環境/灰度環境。

新版的 Dubbo Admin 提供了標籤路由的配置項:

Dubbo 用戶可以在自己系統的基礎上對標籤路由進行二次擴展,或者借鑑標籤路由的設計,實現自己系統的流量隔離,灰度發佈。

總結

本文介紹了 Dubbo 2.7 比較重要的三大新特性:異步化改造,三大中心改造,服務治理增強。Dubbo 2.7 還包含了很多功能優化、特性升級,可以在項目源碼的 CHANGES.md 中瀏覽全部的改動點。最後提供一份 Dubbo 2.7 的升級文檔: 2.7遷移文檔 ,歡迎體驗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章