您好,登錄后才能下訂單哦!
摘要: 導(dǎo)讀: 近日,在Apache Dubbo開(kāi)發(fā)者沙龍杭州站的活動(dòng)中,阿里巴巴中間件技術(shù)專(zhuān)家曹勝利(展圖)向開(kāi)發(fā)者們分享了Dubbo2.7版本的規(guī)劃。 本文將為你探秘 Dubbo 2.7背后的思考和實(shí)現(xiàn)方式。
cdn.com/1533ca3c248d38eee9102c7d3bdfb4cec92642b9.png">
導(dǎo)讀:
近日,在Apache Dubbo開(kāi)發(fā)者沙龍杭州站的活動(dòng)中,阿里巴巴中間件技術(shù)專(zhuān)家曹勝利(展圖)向開(kāi)發(fā)者們分享了Dubbo2.7版本的規(guī)劃。
本文將為你探秘 Dubbo 2.7背后的思考和實(shí)現(xiàn)方式。
作者:(按姓氏拼音排序,排名不分先后)
曹勝利(展圖):Apache Dubbo Committer。
劉軍(陸龜):Apache Dubbo Committer。
Dubbo 2.7 將圍繞 異步支持優(yōu)化、元數(shù)據(jù)改造,引入JDK8的特性、Netty4.0的特性以及MetricsAPI 5個(gè)方面提升服務(wù)調(diào)用和服務(wù)治理的效率,以及可擴(kuò)展性,同時(shí)將修復(fù)社區(qū)提出的若干問(wèn)題。
據(jù)悉,2.7.x會(huì)作為Dubbo在Apache社區(qū)的畢業(yè)版本,Dubbo將有機(jī)會(huì)成為繼RocketMQ后,來(lái)自阿里巴巴的又一個(gè)Apache頂級(jí)項(xiàng)目(TLP)。
基于Dubbo實(shí)現(xiàn)全異步編程,是在2.7.0版本中對(duì)現(xiàn)有異步方式增強(qiáng)后新引入的功能。之前的版本對(duì)異步支持用起來(lái)不是很友好,存在若干問(wèn)題,2.7版本將基于JDK8 中的CompletableFuture做出一些針對(duì)性的增強(qiáng),同時(shí)新增了@Dubboasync的注解,通過(guò)這個(gè)注解可以生成異步化相關(guān)的代碼。
? 2.6.x版本之前的異步方式
在2.6.x及之前的版本提供了一定的異步編程能力,包括Consumer端異步調(diào)用、參數(shù)回調(diào)、事件通知等。但當(dāng)前的異步方式存在以下問(wèn)題:
Future獲取方式不夠直接;
Future接口無(wú)法實(shí)現(xiàn)自動(dòng)回調(diào),而自定義ResponseFuture雖支持回調(diào)但支持的異步場(chǎng)景有限,如不支持Future間的相互協(xié)調(diào)或組合等;
不支持Provider端異步
以Consumer端異步使用方式為例:
1、定義一個(gè)普通的同步接口并聲明支持異步調(diào)用
public interface FooService {String findFoo(String name);} <dubbo:reference id="fooService" interface="com.alibaba.foo.FooService"> <dubbo:method name="findFoo" async="true" /> </dubbo:reference>
2、通過(guò)RpcContext獲取Future
// 此調(diào)用會(huì)立即返回nullfooService.findFoo(fooId);// 拿到調(diào)用的Future引用,當(dāng)結(jié)果返回后,會(huì)被通知和設(shè)置到此FutureFuture<Foo> fooFuture = RpcContext.getContext().getFuture();fooFuture.get();
或
// 此調(diào)用會(huì)立即返回nullfooService.findFoo(fooId);// 拿到Dubbo內(nèi)置的ResponseFuture并設(shè)置回調(diào)ResponseFuture future = ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture();future.setCallback(new ResponseCallback() {@Overridepublic void done(Object response) { System.out.print(response); }@Overridepublic void caught(Throwable exception) { exception.printStackTrace(); }});
從這個(gè)簡(jiǎn)單的示例我們可以體會(huì)到一些使用中的不便之處:
findFoo的同步接口,不能直接返回代表異步結(jié)果的Future,通過(guò)RpcContext進(jìn)一步獲取。
Future只支持阻塞式的get()接口獲取結(jié)果。
通過(guò)獲取內(nèi)置的ResponseFuture接口,可以設(shè)置回調(diào)。但獲取ResponseFuture的API使用不便,且僅支持設(shè)置回調(diào)其他異步場(chǎng)景均不支持,如多個(gè)Future協(xié)同工作的場(chǎng)景等。
? 2.7.0基于CompletableFuture的增強(qiáng)
了解Java中Future演進(jìn)歷史的同學(xué)應(yīng)該知道,Dubbo 2.6.x及之前版本中使用的Future是在Java 5中引入的,所以存在以上一些功能設(shè)計(jì)上的問(wèn)題,而在Java 8中引入的CompletableFuture進(jìn)一步豐富了Future接口,很好的解決了這些問(wèn)題。
Dubbo在2.7.0版本已經(jīng)升級(jí)了對(duì)Java 8的支持,同時(shí)基于CompletableFuture對(duì)當(dāng)前的異步功能進(jìn)行了增強(qiáng)。
1、支持直接定義返回CompletableFuture的服務(wù)接口。通過(guò)這種類(lèi)型的接口,我們可以更自然的實(shí)現(xiàn)Consumer、Provider端的異步編程。
public interface AsyncService {CompletableFuture<String> sayHello(String name);}
2、如果你不想將接口的返回值定義為Future類(lèi)型,或者存在定義好的同步類(lèi)型接口,則可以額外定義一個(gè)異步接口并提供Future類(lèi)型的方法。
public interface GreetingsService {String sayHi(String name);}@AsyncFor(GreetingsService.class)public interface GrettingServiceAsync extends GreetingsService {CompletableFuture<String> sayHiAsync(String name);}
這樣,Provider可以只實(shí)現(xiàn)sayHi方法;而Consumer通過(guò)直接調(diào)用sayHiAsync可以拿到一個(gè)Future實(shí)例,Dubbo框架在Provider端會(huì)自動(dòng)轉(zhuǎn)換為對(duì)sayHi方法的調(diào)用。為每個(gè)同步方法提供一個(gè)異步方法定義會(huì)比較麻煩,更進(jìn)一步的,利用Dubbo生態(tài)中的AnnotationProcessor實(shí)現(xiàn),可以自動(dòng)幫我們自動(dòng)生成異步方法定義。
3、同樣的,如果你的原始接口定義不是Future類(lèi)型的返回值,Provider端異步也提供了類(lèi)似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; }}
在方法體的開(kāi)始RpcContext.startAsync()啟動(dòng)異步,并開(kāi)啟新線(xiàn)程異步的執(zhí)行業(yè)務(wù)邏輯,在耗時(shí)操作完成后通過(guò)asyncContext.write將結(jié)果寫(xiě)回。
4、RpcContext直接返回CompletableFuture
CompletableFuture<String> f = RpcContext.getContext().getCompletableFuture();
以上所有的增強(qiáng),是在兼容已有異步編程的基礎(chǔ)上進(jìn)行的,因此基于2.6.x版本編寫(xiě)的異步程序不用做任何改造即可順利運(yùn)行。
元數(shù)據(jù)的改造主要是從適配微服務(wù)注冊(cè)中心、配置中心分離的模型、減輕注冊(cè)中心壓力、提高服務(wù)治理能力和效率的角度來(lái)執(zhí)行的。目前版本的Dubbo在注冊(cè)中心的URL有數(shù)十個(gè)key/value的鍵值對(duì),包含了一個(gè)服務(wù)的所有元數(shù)據(jù)。在大規(guī)模實(shí)踐的基礎(chǔ)上,我們逐漸發(fā)現(xiàn)這樣組織的元數(shù)據(jù)存在一些問(wèn)題:
注冊(cè)中心存儲(chǔ)的URL過(guò)長(zhǎng):
導(dǎo)致存儲(chǔ)壓力驟增,變更事件的推送效率明顯下降;同時(shí)給訂閱方帶來(lái)了額外的計(jì)算壓力,尤其是大規(guī)模場(chǎng)景下的內(nèi)存,增長(zhǎng)顯著。
注冊(cè)中心承擔(dān)了過(guò)多服務(wù)治理配置的功能:
負(fù)責(zé)初始配置的同步,同時(shí)負(fù)責(zé)存儲(chǔ)各種運(yùn)行期配置規(guī)則。這一方面加劇了注冊(cè)中心的壓力,另一方面配置規(guī)則的靈活性也受到了一定的限制,同時(shí)也無(wú)法利用一些更專(zhuān)業(yè)的微服務(wù)配置中心帶來(lái)的強(qiáng)大功能。
屬性的功能定位不清晰:
methods, pid, owner看起來(lái)都是為服務(wù)查詢(xún)服務(wù)而注冊(cè)的屬性,但當(dāng)我們實(shí)際開(kāi)發(fā)或操作服務(wù)管控系統(tǒng)時(shí),卻發(fā)現(xiàn)這樣簡(jiǎn)陋的信息是很難滿(mǎn)足查詢(xún)治理需求的。我們更多的屬性,需要更豐富的注冊(cè)數(shù)據(jù)。以methods為例,雖然方法列表的內(nèi)容已經(jīng)很長(zhǎng)了,但當(dāng)我們要在OPS開(kāi)發(fā)服務(wù)測(cè)試/mock功能時(shí),卻發(fā)現(xiàn)需要的方法簽名等數(shù)據(jù)還是無(wú)法獲取。
概括以上問(wèn)題,我們將URL中的元數(shù)據(jù)劃分了三個(gè)部分:
元數(shù)據(jù)信息
接口的完整定義:包含接口名,接口所含的方法,以及方法所含的出入?yún)⑿畔ⅰ?duì)于服務(wù)測(cè)試和服務(wù)mock有非常重要的作用。
執(zhí)行鏈路上數(shù)據(jù)
需要將參數(shù)從provider端傳遞給消費(fèi)者端,讓消費(fèi)者端感知到的。如token,timeout等。
服務(wù)自持有配置&Ops需求
只有在provider端或者消費(fèi)者端需要使用的,如executes, document等。
配置中心是dubbo.properties的動(dòng)態(tài)版本,支持的粒度包括全局的、應(yīng)用級(jí)別的和服務(wù)級(jí)別的等維度。通過(guò)上面的元數(shù)據(jù)改造,配置中心支持,再加上原有的注冊(cè)中心,Dubbo體系里就會(huì)存在:
注冊(cè)中心:
理想情況下,注冊(cè)中心將只用于關(guān)鍵服務(wù)信息(核心鏈路)的同步,進(jìn)一步減輕注冊(cè)中心的存儲(chǔ)壓力,提高地址同步效率,同時(shí)緩解當(dāng)前由于URL冗余在大規(guī)模推送時(shí)造成的Consumer端內(nèi)存計(jì)算壓力。
配置中心:
解決當(dāng)前配置和地址信息耦合的問(wèn)題,通過(guò)抽象動(dòng)態(tài)配置層,讓開(kāi)發(fā)者可以對(duì)接微服務(wù)場(chǎng)景下更常用的、更專(zhuān)業(yè)的配置中心,如Nacos, Apollo, Consul, Etcd等;提供更靈活的、更豐富的配置規(guī)則,包括服務(wù)、應(yīng)用不同粒度的配置,更豐富的路由規(guī)則,集中式管理的動(dòng)態(tài)參數(shù)規(guī)則等。
服務(wù)查詢(xún)治理中心(含元數(shù)據(jù))
對(duì)于純粹的服務(wù)查詢(xún)相關(guān)的數(shù)據(jù),包括Consumer的服務(wù)訂閱數(shù)據(jù),往往都是注冊(cè)后不可變的并且不需要節(jié)點(diǎn)間的同步,如當(dāng)前URL可以看到的methods、owner等key以及所有的Consumer端URL。
因此我們?cè)?.7.0中引入了存儲(chǔ)模塊,專(zhuān)門(mén)用來(lái)存放這部分?jǐn)?shù)據(jù),這部分將會(huì)和新版本的Dubbo-ops密切整合,作為豐富的服務(wù)查詢(xún)、測(cè)試等功能的數(shù)據(jù)基礎(chǔ),因此這部分的數(shù)據(jù)將會(huì)得到進(jìn)一步的豐富??傮w來(lái)說(shuō)否開(kāi)啟此功能對(duì)用戶(hù)將是可選的,并且實(shí)現(xiàn)上也將是可擴(kuò)展的,如我們計(jì)劃支持Redis, Zookeeper等。
路由規(guī)則
Dubbo 提供了具有一定擴(kuò)展性的路由規(guī)則,其中具有代表性的是條件路由和腳本路由。2.6.x及以下版本存在的問(wèn)題:
路由規(guī)則存儲(chǔ)在注冊(cè)中心
只支持服務(wù)粒度的路由,應(yīng)用級(jí)別無(wú)法定義路由規(guī)則
支持路由緩存,但基本不具有擴(kuò)展性
一個(gè)服務(wù)或應(yīng)用允許定義多條路由規(guī)則,服務(wù)治理無(wú)法管控
實(shí)現(xiàn)上,每條規(guī)則生成一個(gè)Router實(shí)例并動(dòng)態(tài)加載
從問(wèn)題出發(fā)我們重新設(shè)計(jì),將原來(lái)的路由配置從注冊(cè)中心遷往配置中心。明確了配置和服務(wù)發(fā)現(xiàn)的邊界。新增了RouterChain,用于重構(gòu)路由規(guī)則邏輯,新增應(yīng)用級(jí)別路由,Tag路由優(yōu)化等。針對(duì)服務(wù)級(jí)別的路由,精確到單個(gè)服務(wù),避免了無(wú)法明確路由規(guī)則的問(wèn)題。
我們簡(jiǎn)單概括下各個(gè)類(lèi)的協(xié)作關(guān)系。
RegistryDirectory,包含完整的地址列表,直接對(duì)接注冊(cè)中心,并動(dòng)態(tài)接收注冊(cè)中心地址變更。
RouterChain,由Router組裝成的列表,是路由動(dòng)作的入口,接收傳入的地址列表并將過(guò)濾后的地址列表返回給調(diào)用方,而具體的過(guò)濾動(dòng)作則委托給Router執(zhí)行
Router,接收并解析路由規(guī)則,接收地址列表,根據(jù)路由規(guī)則完成過(guò)濾動(dòng)作,并返回過(guò)濾后的地址列表。其本身也是一個(gè)ConfigurationListener,隨時(shí)接收路由規(guī)則更新。
ConfigurationListener,動(dòng)態(tài)配置變更的回調(diào)接口
DynamicConfiguration,動(dòng)態(tài)配置SPI,支持的擴(kuò)展實(shí)現(xiàn)包括Zookeeper、Apollo、Nacos等
Dubbo 將在近期正式發(fā)布2.7.0版本,恰值Dubbo宣布重啟一周年。這一年,Dubbo 共發(fā)布了13個(gè)版本,社區(qū)共有24位PPMC/Committer,144位Contributor,在北京、上海、深圳、成都和杭州舉辦了5場(chǎng)開(kāi)發(fā)者沙龍,但技術(shù)開(kāi)源的道路并沒(méi)有止境,我們歡迎更多的開(kāi)發(fā)者們可以參與進(jìn)來(lái),并到Dubbo meetup來(lái)進(jìn)行分享,一起建設(shè)Dubbo生態(tài)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。