Dubbo下一站:Apache頂級項目

導讀:編程

近日,在Apache Dubbo開發者沙龍杭州站的活動中,阿里巴巴中間件技術專家曹勝利(展圖)向開發者們分享了Dubbo2.7版本的規劃。緩存

本文將爲你探祕 Dubbo 2.7背後的思考和實現方式。框架

做者:(按姓氏拼音排序,排名不分前後)異步

曹勝利(展圖):Apache Dubbo Committer。async

劉軍(陸龜):Apache Dubbo Committer。ide

Dubbo 2.7 將圍繞 異步支持優化、元數據改造,引入JDK8的特性、Netty4.0的特性以及MetricsAPI 5個方面提高服務調用和服務治理的效率,以及可擴展性,同時將修復社區提出的若干問題。異步編程

據悉,2.7.x會做爲Dubbo在Apache社區的畢業版本,Dubbo將有機會成爲繼RocketMQ後,來自阿里巴巴的又一個Apache頂級項目(TLP)。微服務

優化對異步的支持

基於Dubbo實現全異步編程,是在2.7.0版本中對現有異步方式加強後新引入的功能。以前的版本對異步支持用起來不是很友好,存在若干問題,2.7版本將基於JDK8 中的CompletableFuture作出一些針對性的加強,同時新增了@Dubboasync的註解,經過這個註解能夠生成異步化相關的代碼。測試

» 2.6.x版本以前的異步方式優化

在2.6.x及以前的版本提供了必定的異步編程能力,包括Consumer端異步調用、參數回調、事件通知等。但當前的異步方式存在如下問題:

Future獲取方式不夠直接;

Future接口沒法實現自動回調,而自定義ResponseFuture雖支持回調但支持的異步場景有限,如不支持Future間的相互協調或組合等;

不支持Provider端異步

以Consumer端異步使用方式爲例:

一、定義一個普通的同步接口並聲明支持異步調用

public interface FooService {
String findFoo(String name);}

<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
  <dubbo:method name="findFoo" async="true" /> </dubbo:reference>

二、經過RpcContext獲取Future

// 此調用會當即返回nullfooService.findFoo(fooId);// 拿到調用的Future引用,當結果返回後,會被通知和設置到此FutureFuture<Foo> fooFuture = RpcContext.getContext().getFuture();fooFuture.get();

// 此調用會當即返回nullfooService.findFoo(fooId);// 拿到Dubbo內置的ResponseFuture並設置回調ResponseFuture future = ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();future.setCallback(new ResponseCallback() {
@Override
public void done(Object response) {
    System.out.print(response);
}

@Override
public void caught(Throwable exception) {
    exception.printStackTrace();
}});

從這個簡單的示例咱們能夠體會到一些使用中的不便之處:

  • findFoo的同步接口,不能直接返回表明異步結果的Future,經過RpcContext進一步獲取。
  • Future只支持阻塞式的get()接口獲取結果。
  • 經過獲取內置的ResponseFuture接口,能夠設置回調。但獲取ResponseFuture的API使用不便,且僅支持設置回調其餘異步場景均不支持,如多個Future協同工做的場景等。

» 2.7.0基於CompletableFuture的加強

瞭解Java中Future演進歷史的同窗應該知道,Dubbo 2.6.x及以前版本中使用的Future是在Java 5中引入的,因此存在以上一些功能設計上的問題,而在Java 8中引入的CompletableFuture進一步豐富了Future接口,很好的解決了這些問題。

Dubbo在2.7.0版本已經升級了對Java 8的支持,同時基於CompletableFuture對當前的異步功能進行了加強。

一、支持直接定義返回CompletableFuture的服務接口。經過這種類型的接口,咱們能夠更天然的實現Consumer、Provider端的異步編程。

public interface AsyncService {
CompletableFuture<String> sayHello(String name);}

二、若是你不想將接口的返回值定義爲Future類型,或者存在定義好的同步類型接口,則能夠額外定義一個異步接口並提供Future類型的方法。

public interface GreetingsService {
String sayHi(String name);}

@AsyncFor(GreetingsService.class)public interface GrettingServiceAsync extends GreetingsService {
CompletableFuture<String> sayHiAsync(String name);}

這樣,Provider能夠只實現sayHi方法;而Consumer經過直接調用sayHiAsync能夠拿到一個Future實例,Dubbo框架在Provider端會自動轉換爲對sayHi方法的調用。爲每一個同步方法提供一個異步方法定義會比較麻煩,更進一步的,利用Dubbo生態中的AnnotationProcessor實現,能夠自動幫咱們自動生成異步方法定義。

三、一樣的,若是你的原始接口定義不是Future類型的返回值,Provider端異步也提供了相似Servlet3.0裏的Async Servlet的編程接口: RpcContext.startAsync()。

public interface AsyncService {
String sayHello(String name);}

public class AsyncServiceImpl implements AsyncService {
public String sayHello(String name) {
    final AsyncContext asyncContext = RpcContext.startAsync();
    new Thread(() -> {
        asyncContext.write("Hello " + name + ", response from provider.");
    }).start();
    return null;
}}

在方法體的開始RpcContext.startAsync()啓動異步,並開啓新線程異步的執行業務邏輯,在耗時操做完成後經過asyncContext.write將結果寫回。

四、RpcContext直接返回CompletableFuture

CompletableFuture<String> f = RpcContext.getContext().getCompletableFuture();

以上全部的加強,是在兼容已有異步編程的基礎上進行的,所以基於2.6.x版本編寫的異步程序不用作任何改造便可順利運行。

元數據改造

元數據的改造主要是從適配微服務註冊中心、配置中心分離的模型、減輕註冊中心壓力、提升服務治理能力和效率的角度來執行的。目前版本的Dubbo在註冊中心的URL有數十個key/value的鍵值對,包含了一個服務的全部元數據。在大規模實踐的基礎上,咱們逐漸發現這樣組織的元數據存在一些問題:

  • 註冊中心存儲的URL過長:

致使存儲壓力驟增,變動事件的推送效率明顯降低;同時給訂閱方帶來了額外的計算壓力,尤爲是大規模場景下的內存,增加顯著。

  • 註冊中心承擔了過多服務治理配置的功能:

負責初始配置的同步,同時負責存儲各類運行期配置規則。這一方面加重了註冊中心的壓力,另外一方面配置規則的靈活性也受到了必定的限制,同時也沒法利用一些更專業的微服務配置中心帶來的強大功能。

  • 屬性的功能定位不清晰:

methods, pid, owner看起來都是爲服務查詢服務而註冊的屬性,但當咱們實際開發或操做服務管控系統時,卻發現這樣簡陋的信息是很難知足查詢治理需求的。咱們更多的屬性,須要更豐富的註冊數據。以methods爲例,雖然方法列表的內容已經很長了,但當咱們要在OPS開發服務測試/mock功能時,卻發現須要的方法簽名等數據仍是沒法獲取。

歸納以上問題,咱們將URL中的元數據劃分了三個部分:

  • 元數據信息

接口的完整定義:包含接口名,接口所含的方法,以及方法所含的出入參信息。對於服務測試和服務mock有很是重要的做用。

  • 執行鏈路上數據

須要將參數從provider端傳遞給消費者端,讓消費者端感知到的。如token,timeout等。

  • 服務自持有配置&Ops需求

只有在provider端或者消費者端須要使用的,如executes, document等。

支持配置中心

配置中心是dubbo.properties的動態版本,支持的粒度包括全局的、應用級別的和服務級別的等維度。經過上面的元數據改造,配置中心支持,再加上原有的註冊中心,Dubbo體系裏就會存在:

  • 註冊中心:

理想狀況下,註冊中心將只用於關鍵服務信息(核心鏈路)的同步,進一步減輕註冊中心的存儲壓力,提升地址同步效率,同時緩解當前因爲URL冗餘在大規模推送時形成的Consumer端內存計算壓力。

  • 配置中心:

解決當前配置和地址信息耦合的問題,經過抽象動態配置層,讓開發者能夠對接微服務場景下更經常使用的、更專業的配置中心,如Nacos, Apollo, Consul, Etcd等;提供更靈活的、更豐富的配置規則,包括服務、應用不一樣粒度的配置,更豐富的路由規則,集中式管理的動態參數規則等。

  • 服務查詢治理中心(含元數據)

對於純粹的服務查詢相關的數據,包括Consumer的服務訂閱數據,每每都是註冊後不可變的而且不須要節點間的同步,如當前URL能夠看到的methods、owner等key以及全部的Consumer端URL。

所以咱們在2.7.0中引入了存儲模塊,專門用來存放這部分數據,這部分將會和新版本的Dubbo-ops密切整合,做爲豐富的服務查詢、測試等功能的數據基礎,所以這部分的數據將會獲得進一步的豐富。整體來講否開啓此功能對用戶將是可選的,而且實現上也將是可擴展的,如咱們計劃支持Redis, Zookeeper等。

  • 路由規則

Dubbo 提供了具備必定擴展性的路由規則,其中具備表明性的是條件路由和腳本路由。2.6.x及如下版本存在的問題:

  1. 路由規則存儲在註冊中心
  2. 只支持服務粒度的路由,應用級別沒法定義路由規則
  3. 支持路由緩存,但基本不具備擴展性
  4. 一個服務或應用容許定義多條路由規則,服務治理沒法管控
  5. 實現上,每條規則生成一個Router實例並動態加載

從問題出發咱們從新設計,將原來的路由配置從註冊中心遷往配置中心。明確了配置和服務發現的邊界。新增了RouterChain,用於重構路由規則邏輯,新增應用級別路由,Tag路由優化等。針對服務級別的路由,精確到單個服務,避免了沒法明確路由規則的問題。

咱們簡單歸納下各個類的協做關係。

  • RegistryDirectory,包含完整的地址列表,直接對接註冊中心,並動態接收註冊中心地址變動。
  • RouterChain,由Router組裝成的列表,是路由動做的入口,接收傳入的地址列表並將過濾後的地址列表返回給調用方,而具體的過濾動做則委託給Router執行
  • Router,接收並解析路由規則,接收地址列表,根據路由規則完成過濾動做,並返回過濾後的地址列表。其自己也是一個ConfigurationListener,隨時接收路由規則更新。
  • ConfigurationListener,動態配置變動的回調接口
  • DynamicConfiguration,動態配置SPI,支持的擴展實現包括Zookeeper、Apollo、Nacos等

Dubbo 將在近期正式發佈2.7.0版本,恰值Dubbo宣佈重啓一週年。這一年,Dubbo 共發佈了13個版本,社區共有24位PPMC/Committer,144位Contributor,在北京、上海、深圳、成都和杭州舉辦了5場開發者沙龍,但技術開源的道路並無止境,咱們歡迎更多的開發者們能夠參與進來,併到Dubbo meetup來進行分享,一塊兒建設Dubbo生態。

原文連接

相關文章
相關標籤/搜索