dubbo是什么
dubbo是一個(gè)分布式服務(wù)中間件,是高性能和透明化的RPC遠(yuǎn)程服務(wù)調(diào)用解決方案,主要通過資源調(diào)度和服務(wù)治理來解決分布式架構(gòu)下服務(wù)資源浪費(fèi)以提高集群的使用率。核心部分包含:
- 遠(yuǎn)程通訊:提供多種基于長連接的NIO的抽象封裝,包括多種線程模型,序列化方式,以及請求-響應(yīng)模式的信息交互
- 集群容錯:提供基于接口方法的透明化遠(yuǎn)程調(diào)用,包括多協(xié)議支持,軟負(fù)載均衡,失敗容錯,地址路由,動態(tài)配置的集群策略
- 服務(wù)發(fā)現(xiàn):基于注冊中心目錄服務(wù),使服務(wù)消費(fèi)者能動態(tài)查找服務(wù)提供者,使地址透明,服務(wù)提供者可以根據(jù)流量平滑增加節(jié)點(diǎn)
1.dubbo架構(gòu)-組件角色
在深入架構(gòu)前,先了解一下dubbo的組件角色,如下圖:

- Provider:暴露服務(wù)的服務(wù)提供方
- Container:服務(wù)運(yùn)行容器
- Registry:服務(wù)注冊與發(fā)現(xiàn)的注冊中心
- Consumer:調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方
- Monitor:統(tǒng)計(jì)服務(wù)調(diào)用次數(shù)和耗時(shí)的監(jiān)控中心
調(diào)用關(guān)系分析:
- 服務(wù)容器Container負(fù)責(zé)啟動,加載,運(yùn)行服務(wù)提供者。
- 服務(wù)提供者Provider在啟動時(shí),向注冊中心注冊自己提供的服務(wù)。
- 服務(wù)消費(fèi)者Consumer在啟動時(shí),向注冊中心訂閱自己所需的服務(wù)。
- 注冊中心Registry返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
- 服務(wù)消費(fèi)者Consumer,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺調(diào)用。
- 服務(wù)消費(fèi)者Consumer和提供者Provider,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心Monitor。

問:各組件角色之間是采用長連接還是短連接交互的?為什么? 答:服務(wù)提供者->消費(fèi)者,服務(wù)提供者->注冊中心,服務(wù)消費(fèi)者->注冊中心。以上三個(gè)交互環(huán)節(jié)采用的是長連接。至于為什么?筆者分析是因?yàn)榛谧灾行牡膭討B(tài)服務(wù)發(fā)現(xiàn)與注冊機(jī)制,所以需要通過心跳的方式實(shí)時(shí)檢測節(jié)點(diǎn)的狀態(tài)
2.dubbo架構(gòu)-模型分層
先來看一下dubbo這個(gè)中間件的領(lǐng)域模型分層結(jié)構(gòu),如下圖:

中間件架構(gòu)分層設(shè)計(jì):
- 服務(wù)接口層(Service):該層是與實(shí)際業(yè)務(wù)邏輯相關(guān)的,根據(jù)服務(wù)提供方和服務(wù)消費(fèi)方的業(yè)務(wù)設(shè)計(jì)對應(yīng)的接口和實(shí)現(xiàn)。
- 配置層(Config):對外配置接口,以ServiceConfig和ReferenceConfig為中心,可以直接new配置類,也可以通過spring解析配置生成配置類。
- 服務(wù)代理層(Proxy):服務(wù)接口透明代理,生成服務(wù)的客戶端Stub和服務(wù)器端Skeleton,以ServiceProxy為中心,擴(kuò)展接口為ProxyFactory。
- 服務(wù)注冊層(Registry):封裝服務(wù)地址的注冊與發(fā)現(xiàn),以服務(wù)URL為中心,擴(kuò)展接口為RegistryFactory、Registry和RegistryService。可能沒有服務(wù)注冊中心,此時(shí)服務(wù)提供方直接暴露服務(wù)。
- 集群層(Cluster):封裝多個(gè)提供者的路由及負(fù)載均衡,并橋接注冊中心,以Invoker為中心,擴(kuò)展接口為Cluster、 Directory、Router和LoadBalance。將多個(gè)服務(wù)提供方組合為一個(gè)服務(wù)提供方,實(shí)現(xiàn)對服務(wù)消費(fèi)方來透明,只需要與一個(gè)服務(wù)提供方進(jìn) 行交互。
- 監(jiān)控層(Monitor):RPC調(diào)用次數(shù)和調(diào)用時(shí)間監(jiān)控,以Statistics為中心,擴(kuò)展接口為MonitorFactory、Monitor和MonitorService。
- 遠(yuǎn)程調(diào)用層(Protocol):封將RPC調(diào)用,以Invocation和Result為中心,擴(kuò)展接口為Protocol、Invoker和 Exporter。Protocol是服務(wù)域,它是Invoker暴露和引用的主功能入口,它負(fù)責(zé)Invoker的生命周期管理。Invoker是實(shí)體 域,它是Dubbo的核心模型,其它模型都向它靠擾,或轉(zhuǎn)換成它,它代表一個(gè)可執(zhí)行體,可向它發(fā)起invoke調(diào)用,它有可能是一個(gè)本地的實(shí)現(xiàn),也可能是 一個(gè)遠(yuǎn)程的實(shí)現(xiàn),也可能一個(gè)集群實(shí)現(xiàn)。
- 信息交換層(Exchange):封裝請求響應(yīng)模式,同步轉(zhuǎn)異步,以Request和Response為中心,擴(kuò)展接口為Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer。
- 網(wǎng)絡(luò)傳輸層(Transport):抽象mina和netty為統(tǒng)一接口,以Message為中心,擴(kuò)展接口為Channel、Transporter、Client、Server和Codec。
- 數(shù)據(jù)序列化層(Serialize):可復(fù)用的一些工具,擴(kuò)展接口為Serialization、 ObjectInput、ObjectOutput和ThreadPool。
各層之間關(guān)系總結(jié):
- Protocol是核心層,簡單組合只需要Protocol,Invoker,Exporter就能完成遠(yuǎn)程方法調(diào)用。Filter是在Invoker過程中增加的攔截層
- Registry和Monitor屬于獨(dú)立角色,Registry屬于注冊中心層
- Consumer和Provider屬于抽象概念,因?yàn)橐粋€(gè)節(jié)點(diǎn)在提供服務(wù)的同時(shí)調(diào)用了別的服務(wù),那么他既屬于Consumer又屬于Provider
- Proxy層封裝過了所有接口了透明代理,但是其他層都以Invoker為中心,只有暴露接口時(shí)才用Proxy將Invoker轉(zhuǎn)換為接口(接口轉(zhuǎn)換為Invoker)
- Remoting是dubbo協(xié)議的實(shí)現(xiàn),內(nèi)部分為Transport傳輸層和Exchange信息交換層,Transport只負(fù)責(zé)單向數(shù)據(jù)傳輸(Netty,Mina的抽象,也可以擴(kuò)展使用UDP協(xié)議傳輸),Exchange是在Transport上定義了 Request-Response的模型
3.調(diào)用鏈路
在看源碼之前,先看一下dubbo服務(wù)調(diào)用鏈路過程。如下圖:

首先服務(wù)消費(fèi)者通過代理對象 Proxy 發(fā)起遠(yuǎn)程調(diào)用,接著通過網(wǎng)絡(luò)客戶端 Client 將編碼后的請求發(fā)送給服務(wù)提供方的網(wǎng)絡(luò)層上,也就是 Server。Server 在收到請求后,首先要做的事情是對數(shù)據(jù)包進(jìn)行解碼。然后將解碼后的請求發(fā)送至分發(fā)器 Dispatcher,再由分發(fā)器將請求派發(fā)到指定的線程池上,最后由線程池調(diào)用具體的服務(wù)。這就是一個(gè)遠(yuǎn)程調(diào)用請求的發(fā)送與接收過程。
3.1.調(diào)用方式
dubbo支持同步和異步兩種調(diào)用方式,其中異步調(diào)用分為“有返回值”的異步調(diào)用和“無返回值”的異步調(diào)用。所謂“無返回值”異步調(diào)用是指服務(wù)消費(fèi)方只管調(diào)用,但不關(guān)心調(diào)用結(jié)果,此時(shí) dubbo 會直接返回一個(gè)空的 RpcResult。若要使用異步特性,需要服務(wù)消費(fèi)方手動進(jìn)行配置。默認(rèn)情況下,Dubbo 使用同步調(diào)用方式。
異步調(diào)用方式如下:
/* 異步方法配置 */ <dubbo:reference id="asyncService" check="false" interface="com.alibaba.dubbo.demo.AsyncService" url="localhost:20880"> <dubbo:method name="sayHello" async="true" /> </dubbo:reference>
Consumer端調(diào)用方式如下:
- 調(diào)用直接返回null
String result = asyncService.sayHello("world");
- 通過RpcContext獲取Future對象,調(diào)用get()阻塞直到返回結(jié)果
asyncService.sayHello("world"); Future<String> future = RpcContext.getContext().getFuture(); String result = future.get();
- 通過ResponseFuture設(shè)置回調(diào),執(zhí)行完成調(diào)用done(),異常調(diào)用caught()
asyncService.sayHello("world"); ResponseFuture responseFuture = ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture(); responseFuture.setCallback(new ResponseCallback() { @Override public void done(Object response) { System.out.println("done"); } @Override public void caught(Throwable exception) { System.out.println("caught"); } }); try { System.out.println("result = " + responseFuture.get()); } catch (RemotingException e) { e.printStackTrace(); }
4.Dubbo VS SpringCloud VS Other

Dubbo實(shí)現(xiàn)了服務(wù)治理的基礎(chǔ),但是要完成一個(gè)完備的微服務(wù)架構(gòu),還需要在各環(huán)節(jié)去擴(kuò)展和完善以保證集群的健康,以減輕開發(fā)、測試以及運(yùn)維各個(gè)環(huán)節(jié)上增加出來的壓力,這樣才能讓各環(huán)節(jié)人員真正的專注于業(yè)務(wù)邏輯。而Spring Cloud依然發(fā)揚(yáng)了Spring Source整合一切的作風(fēng),以標(biāo)準(zhǔn)化的姿態(tài)將一些微服務(wù)架構(gòu)的成熟產(chǎn)品與框架揉為一體,并繼承了Spring Boot簡單配置、快速開發(fā)、輕松部署的特點(diǎn),讓原本復(fù)雜的架構(gòu)工作變得相對容易上手一些。所以,如果選擇Dubbo請務(wù)必在各個(gè)環(huán)節(jié)做好整套解決方案的準(zhǔn)備,不然很可能隨著服務(wù)數(shù)量的增長,整個(gè)團(tuán)隊(duì)都將疲于應(yīng)付各種架構(gòu)上不足引起的困難。而如果選擇Spring Cloud,相對來說每個(gè)環(huán)節(jié)都已經(jīng)有了對應(yīng)的組件支持,可能有些也不一定能滿足你所有的需求。
5.踩坑實(shí)例
5.1.Reference注解
使用@Reference注解在消費(fèi)者端注入提供者依賴的時(shí)候,MVC容器獲取對象時(shí)可能出現(xiàn)Null的情況。Reference注解生成的實(shí)例是不會交給Spring容器托管的,只是作為Spring管理bean的一個(gè)屬性賦值去操作,所以無法通過BeanFactory.getBean()獲得。如果容器初始化時(shí)先加載了MVC容器,隨后加載Dubbo實(shí)例,這時(shí)在Controller層引用的dubbo對象會造成空指針的異常,所以加載時(shí)需要進(jìn)行編排,先加載dubbo實(shí)例,再加載MVC容器。另外一種方案是設(shè)計(jì)一個(gè)組件層,將dubbo和業(yè)務(wù)層在組件層進(jìn)行組裝,提供Controller層使用。
5.2.Dubbo超時(shí)和重連機(jī)制
Dubbo默認(rèn)有超時(shí)和重連機(jī)制,在異常場景下如果不注意會引起異常或者冪等性的異常情況。超時(shí)機(jī)制是在指定時(shí)間內(nèi)Provider沒有返回,則認(rèn)定本次調(diào)用失敗。默認(rèn)重連2次,超時(shí)1000ms。另外Dubbo可以以服務(wù),接口,方法3個(gè)緯度配置參數(shù),值得注意的是配置以消費(fèi)者端為準(zhǔn),如果消費(fèi)者端未配置則以提供者端為準(zhǔn)。
5.3.Dubbo序列化協(xié)議
Dubbo適合高并發(fā)量小數(shù)據(jù)傳輸?shù)腞PC場景,默認(rèn)dubbo協(xié)議交互,但是出于性能考慮很多公司會采用kryo序列化方式來提升性能,kryo確實(shí)性能要高出很多,但是有一個(gè)弊端,就是序列化對象修改后不能向上向下兼容,換句話來說就是DTO對象增加了一個(gè)字段,服務(wù)提供者和消費(fèi)者兩端需要同時(shí)升級版本,否則接口調(diào)用就會報(bào)錯。這里可以自己擴(kuò)展Dubbo預(yù)留的Serialization接口在kryo的基礎(chǔ)上完善序列化協(xié)議。