1 架構(gòu)
Dubbo中的五個基礎(chǔ)組件,圖中的紫色線條代表了組件初始化的路徑(init)因宇,藍(lán)色虛線是異步通知流程(async)嗡官,藍(lán)色實線則是同步阻塞調(diào)用(sync)寿羞。
Registry:注冊中心
Provider:服務(wù)提供方
Consumer:向Provider發(fā)起遠(yuǎn)程調(diào)用的消費(fèi)者
Monitor:監(jiān)控中心,用來統(tǒng)計服務(wù)調(diào)用的頻率和響應(yīng)時間
Container:運(yùn)行服務(wù)的容器
服務(wù)發(fā)現(xiàn)的其中一個核心組件是注冊中心矫废,Provider 注冊地址到注冊中心盏缤,Consumer 從注冊中心讀取和訂閱 Provider 地址列表。那我們具體來看一下RPC調(diào)用的具體過程:
start:服務(wù)容器啟動后初始化服務(wù)提供者
register:服務(wù)提供者在啟動的過程中蓖扑,向注冊中心發(fā)起注冊唉铜,進(jìn)行地址的注冊
subscribe:服務(wù)消費(fèi)者在啟動的同時,向注冊中心訂閱所需的服務(wù)律杠。采用的是Pub/Sub模式潭流,也就是發(fā)布訂閱模型
notify:注冊中心將Provider地址列表推送給消費(fèi)者,對于服務(wù)下線之類的變更柜去,注冊中心會主動推送變更數(shù)據(jù)到Consumer(建立在長連接之上)
invoke:服務(wù)消費(fèi)者發(fā)起遠(yuǎn)程調(diào)用灰嫉,這個過程會使用負(fù)載均衡算法挑選目標(biāo)服務(wù)器
count:Consumer和Provider每隔一段時間將統(tǒng)計信息發(fā)送到監(jiān)控中心,平時這些信息就暫存于內(nèi)存當(dāng)中嗓奢。
2 思維導(dǎo)圖
3 常見面試題
3.1 Dubbo 的整體架構(gòu)設(shè)計有哪些分層?
接口服務(wù)層(Service):該層與業(yè)務(wù)邏輯相關(guān)讼撒,根據(jù) provider 和 consumer 的業(yè)務(wù)設(shè)計對應(yīng)的接口和實現(xiàn)
配置層(Config):對外配置接口,以 ServiceConfig 和 ReferenceConfig 為中心
服務(wù)代理層(Proxy):服務(wù)接口透明代理,生成服務(wù)的客戶端 Stub 和 服務(wù)端的 Skeleton根盒,以 ServiceProxy 為中心钳幅,擴(kuò)展接口為 ProxyFactory
服務(wù)注冊層(Registry):封裝服務(wù)地址的注冊和發(fā)現(xiàn),以服務(wù) URL 為中心炎滞,擴(kuò)展接口為 RegistryFactory敢艰、Registry、RegistryService
路由層(Cluster):封裝多個提供者的路由和負(fù)載均衡厂榛,并橋接注冊中心盖矫,以Invoker 為中心,擴(kuò)展接口為 Cluster击奶、Directory辈双、Router 和 LoadBlancce
監(jiān)控層(Monitor):RPC 調(diào)用次數(shù)和調(diào)用時間監(jiān)控,以 Statistics 為中心柜砾,擴(kuò)展接口為 MonitorFactory湃望、Monitor 和 MonitorService
遠(yuǎn)程調(diào)用層(Protocal):封裝 RPC 調(diào)用,以 Invocation 和 Result 為中心痰驱,擴(kuò)展接口為 Protocal证芭、Invoker 和 Exporter
信息交換層(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
Dubbo 的核心功能警检,主要就是如下 3 個核心功能:
Remoting:網(wǎng)絡(luò)通信框架孙援,提供對多種NIO框架抽象封裝,包括 “同步轉(zhuǎn)異步”和“請求-響應(yīng)”模式的信息交換方式扇雕。
Cluster:服務(wù)框架赃磨,提供基于接口方法的透明遠(yuǎn)程過程調(diào)用,包括多 協(xié)議支持洼裤,以及軟負(fù)載均衡邻辉,失敗容錯溪王,地址路由,動態(tài)配置等集群 支持值骇。
Registry:服務(wù)注冊莹菱,基于注冊中心目錄服務(wù),使服務(wù)消費(fèi)方能動態(tài) 的查找服務(wù)提供方吱瘩,使地址透明道伟,使服務(wù)提供方可以平滑增加或減少 機(jī)器。
3.2 默認(rèn)使用的是什么通信框架使碾,還有別的選擇嗎?
默認(rèn)也推薦使用 netty 框架蜜徽,還有 mina。
3.3 服務(wù)調(diào)用是阻塞的嗎票摇?
默認(rèn)是同步等待結(jié)果阻塞的拘鞋,支持異步調(diào)用。 Dubbo是基于NIO的非阻塞實現(xiàn)并行調(diào)用矢门,客戶端不需要啟動多線程即可完成并行調(diào)用多個遠(yuǎn)程服務(wù)盆色,相對多線程開銷較小,異步調(diào)用會返回一個Future對象祟剔。 異步調(diào)用流程圖如下:
3.4 Dubbo默認(rèn)使用什么注冊中心隔躲,還有別的選擇嗎?
默認(rèn)采用 Zookeeper
推薦使用 Nacos 作為注冊中心物延,還有Zookeeper宣旱, Redis、Multicast叛薯、Simple 注冊中心浑吟,但不推薦。Zookeeper是強(qiáng)一致性案训,會出現(xiàn)服務(wù)不可用的情況买置。
3.5 Dubbo支持的序列化協(xié)議粪糙?
Dubbo 支持 hession强霎、Java 二進(jìn)制序列化、json蓉冈、SOAP 文本序列化多種序列化協(xié)議城舞。但是 hession 是其默認(rèn)的序列化協(xié)議
3.6 Dubbo支持的通信協(xié)議?Dubbo調(diào)用RPC有幾種協(xié)議可以用寞酿?
-
Dubbo 協(xié)議
默認(rèn)就是 Dubbo 協(xié)議家夺,單一長連接,進(jìn)行的是 NIO 異步通信伐弹,基于 hessian 作為序列化協(xié)議拉馋。使用的場景是:傳輸數(shù)據(jù)量小(每次請求在 100kb 以內(nèi)),但是并發(fā)量很高煌茴。以及消費(fèi)者遠(yuǎn)大于提供者随闺。
為了要支持高并發(fā)場景,一般是服務(wù)提供者就幾臺機(jī)器蔓腐,但是服務(wù)消費(fèi)者有上百臺矩乐,可能每天調(diào)用量達(dá)到上億次!此時用長連接是最合適的回论,就是跟每個服務(wù)消費(fèi)者維持一個長連接就可以散罕,可能總共就 100 個連接。然后后面直接基于長連接 NIO 異步通信傀蓉,可以支撐高并發(fā)請求欧漱。
-
rmi 協(xié)議
采用JDK標(biāo)準(zhǔn)的rmi協(xié)議實現(xiàn),傳輸參數(shù)和返回參數(shù)對象需要實現(xiàn)Serializable接口僚害,使用java標(biāo)準(zhǔn)序列化機(jī)制硫椰,傳輸數(shù)據(jù)包大小混合,消費(fèi)者和提供者個數(shù)差不多萨蚕,可傳文件靶草,傳輸協(xié)議TCP。
Java 二進(jìn)制序列化岳遥,多個短連接奕翔,適合消費(fèi)者和提供者數(shù)量差不多的情況,適用于文件的傳輸浩蓉,一般較少用派继。
-
hessian 協(xié)議
hessian 序列化協(xié)議,多個短連接捻艳,適用于提供者數(shù)量比消費(fèi)者數(shù)量還多的情況驾窟,適用于文件的傳輸,一般較少用认轨。
集成Hessian服務(wù)绅络,基于HTTP通訊,采用Servlet暴露服務(wù)嘁字,Dubbo內(nèi)嵌Jetty作為服務(wù)器時默認(rèn)實現(xiàn)恩急,提供與Hession服務(wù)互操作。
-
http 協(xié)議
json 序列化纪蜒。
基于Http表單提交的遠(yuǎn)程調(diào)用協(xié)議衷恭,使用Spring的HttpInvoke實現(xiàn)。多個短連接纯续,傳輸協(xié)議HTTP随珠,傳入?yún)?shù)大小混合灭袁,提供者個數(shù)多于消費(fèi)者,需要給應(yīng)用程序和瀏覽器JS調(diào)用窗看;
-
webservice
SOAP 文本序列化简卧。
基于WebService的遠(yuǎn)程調(diào)用協(xié)議,集成CXF實現(xiàn)烤芦,提供和原生WebService的互操作举娩。多個短連接,基于HTTP傳輸构罗,同步傳輸铜涉,適用系統(tǒng)集成和跨語言調(diào)用;
-
memcache
基于memcached實現(xiàn)的RPC協(xié)議
-
redis
基于redis實現(xiàn)的RPC協(xié)議
-
thrift 協(xié)議
當(dāng)前 Dubbo 支持的 thrift 協(xié)議是對 thrift 原生協(xié)議 [2] 的擴(kuò)展遂唧,在原生協(xié)議的基礎(chǔ)上添加了一些額外的頭信息芙代,比如 service name,magic number 等盖彭。
-
RESTful
基于標(biāo)準(zhǔn)的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的簡寫)實現(xiàn)的REST調(diào)用支持纹烹。
3.7 服務(wù)提供者能實現(xiàn)失效踢出是什么原理?
服務(wù)失效踢出基于 zookeeper 的臨時節(jié)點(diǎn)原理召边。
3.8 如何解決服務(wù)調(diào)用鏈過長的問題铺呵?
可以結(jié)合 zipkin 實現(xiàn)分布式服務(wù)追蹤。
3.9 說說核心的配置有哪些隧熙?
在 Provider 上可以配置的 Consumer 端的屬性有哪些片挂?
timeout:方法調(diào)用超時
retries:失敗重試次數(shù),默認(rèn)重試2次
loadbalance:負(fù)載均衡算法贞盯,默認(rèn)隨機(jī)
actives:消費(fèi)者端音念,最大并發(fā)調(diào)用限制
3.10 同一個服務(wù)多個注冊的情況下可以直連某一個服務(wù)嗎?
可以點(diǎn)對點(diǎn)直連躏敢,修改配置即可闷愤,也可以通過 telnet 直接某個服務(wù)。
3.11 Dubbo 集群容錯有幾種方案件余?
3.12 Dubbo 服務(wù)降級讥脐,失敗重試怎么做?
Dubbo 2.2.0 以上版本支持蛾扇。
Dubbo提供了mock配置攘烛,可以很好的實現(xiàn)Dubbo服務(wù)降級魏滚。mock只在出現(xiàn)非業(yè)務(wù)異常(比如超時镀首,網(wǎng)絡(luò)異常等)時執(zhí)行。
-
mock的配置支持兩種
Mock Class降級處理:boolean值鼠次,默認(rèn)的為false更哄。如果配置為true芋齿,則缺省使用mock類名,即類名+Mock后綴成翩;
Mock Null降級處理:配置"return null"觅捆,可以很簡單的忽略掉異常。
3.13 Dubbo Monitor 實現(xiàn)原理麻敌?
Consumer 端在發(fā)起調(diào)用之前會先走 filter 鏈栅炒;provider 端在接收到請求時也是先走 filter 鏈,然后才進(jìn)行真正的業(yè)務(wù)邏輯處理术羔。默認(rèn)情況下赢赊,在 consumer 和 provider 的 filter 鏈中都會有 Monitorfilter。
MonitorFilter 向 DubboMonitor 發(fā)送數(shù)據(jù)
DubboMonitor 將數(shù)據(jù)進(jìn)行聚合后(默認(rèn)聚合 1min 中的統(tǒng)計數(shù)據(jù))暫存到ConcurrentMap<Statistics, AtomicReference> statisticsMap级历,然后使用一個含有 3 個線程(線程名字:DubboMonitorSendTimer)的線程池每隔 1min 鐘释移,調(diào)用 SimpleMonitorService 遍歷發(fā)送 statisticsMap 中的統(tǒng)計數(shù)據(jù),每發(fā)送完畢一個寥殖,就重置當(dāng)前的 Statistics 的 AtomicReference
SimpleMonitorService 將這些聚合數(shù)據(jù)塞入 BlockingQueue queue 中(隊列大寫為 100000)
SimpleMonitorService 使用一個后臺線程(線程名為:DubboMonitorAsyncWriteLogThread)將 queue 中的數(shù)據(jù)寫入文件(該線程以死循環(huán)的形式來寫)
SimpleMonitorService 還會使用一個含有 1 個線程(線程名字:DubboMonitorTimer)的線程池每隔 5min 鐘玩讳,將文件中的統(tǒng)計數(shù)據(jù)畫成圖表
3.14 Dubbo 用到哪些設(shè)計模式?
Dubbo 框架在初始化和通信過程中使用了多種設(shè)計模式嚼贡,可靈活控制類加載熏纯、權(quán)限控制等功能。 工廠模式 Provider 在 export 服務(wù)時粤策,會調(diào)用 ServiceConfig 的 export 方法豆巨。ServiceConfig中有個字段:
private static final Protocol protocol =
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtensi
on();
Dubbo 里有很多這種代碼。這也是一種工廠模式掐场,只是實現(xiàn)類的獲取采用了 JDKSPI 的機(jī)制往扔。這么實現(xiàn)的優(yōu)點(diǎn)是可擴(kuò)展性強(qiáng),想要擴(kuò)展實現(xiàn)熊户,只需要在 classpath下增加個文件就可以了萍膛,代碼零侵入。另外嚷堡,像上面的 Adaptive 實現(xiàn)蝗罗,可以做到調(diào)用時動態(tài)決定調(diào)用哪個實現(xiàn),但是由于這種實現(xiàn)采用了動態(tài)代理蝌戒,會造成代碼調(diào)試比較麻煩串塑,需要分析出實際調(diào)用的實現(xiàn)類。
裝飾器模式
Dubbo 在啟動和調(diào)用階段都大量使用了裝飾器模式北苟。以 Provider 提供的調(diào)用鏈為例桩匪,具體的調(diào)用鏈代碼是在 ProtocolFilterWrapper 的 buildInvokerChain 完成的,具體是將注解中含有 group=provider 的 Filter 實現(xiàn)友鼻,按照 order 排序傻昙,最后的調(diào)用順序是:
EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter ->
ExecuteLimitFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter ->
ExceptionFilter
更確切地說闺骚,這里是裝飾器和責(zé)任鏈模式的混合使用。例如妆档,EchoFilter 的作用是判斷是否是回聲測試請求僻爽,是的話直接返回內(nèi)容,這是一種責(zé)任鏈的體現(xiàn)贾惦。而像ClassLoaderFilter 則只是在主功能上添加了功能荐绝,更改當(dāng)前線程的 ClassLoader朱灿,這是典型的裝飾器模式。
觀察者模式
Dubbo 的 Provider 啟動時,需要與注冊中心交互褪子,先注冊自己的服務(wù)易核,再訂閱自己的服務(wù)嗅蔬,訂閱時蜘渣,采用了觀察者模式,開啟一個 listener杰刽。注冊中心會每 5 秒定時檢查是否有服務(wù)更新菠发,如果有更新,向該服務(wù)的提供者發(fā)送一個 notify 消息贺嫂,provider 接受到 notify 消息后滓鸠,運(yùn)行 NotifyListener 的 notify 方法,執(zhí)行監(jiān)聽器方法第喳。
動態(tài)代理模式
Dubbo 擴(kuò)展 JDK SPI 的類 ExtensionLoader 的 Adaptive 實現(xiàn)是典型的動態(tài)代理實現(xiàn)糜俗。Dubbo 需要靈活地控制實現(xiàn)類,即在調(diào)用階段動態(tài)地根據(jù)參數(shù)決定調(diào)用哪個實現(xiàn)類曲饱,所以采用先生成代理類的方法悠抹,能夠做到靈活的調(diào)用。生成代理類的代碼是 ExtensionLoader 的 createAdaptiveExtensionClassCode 方法扩淀。代理類主要邏輯是楔敌,獲取 URL 參數(shù)中指定參數(shù)的值作為獲取實現(xiàn)類的 key。
3.15 Dubbo 配置文件是如何加載到 Spring 中的驻谆?
Spring 容器在啟動的時候卵凑,會讀取到 Spring 默認(rèn)的一些 schema 以及 Dubbo 自定義的 schema,每個 schema 都會對應(yīng)一個自己的 NamespaceHandler胜臊,NamespaceHandler 里面通過 BeanDefinitionParser 來解析配置信息并轉(zhuǎn)化為需要加載的 bean 對象勺卢!
3.16 Dubbo SPI 和 Java SPI 區(qū)別?
JDK SPI: JDK 標(biāo)準(zhǔn)的 SPI 會一次性加載所有的擴(kuò)展實現(xiàn)象对,如果有的擴(kuò)展吃實話很耗時黑忱,但也沒用上,很浪費(fèi)資源。所以只希望加載某個的實現(xiàn)杨何,就不現(xiàn)實了
DUBBO SPI:
1、對 Dubbo 進(jìn)行擴(kuò)展沥邻,不需要改動 Dubbo 的源碼
2危虱、延遲加載,可以一次只加載自己想要加載的擴(kuò)展實現(xiàn)唐全。
3埃跷、增加了對擴(kuò)展點(diǎn) IOC 和 AOP 的支持,一個擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)邮利。
4弥雹、Dubbo 的擴(kuò)展機(jī)制能很好的支持第三方 IoC 容器,默認(rèn)支持 Spring Bean延届。
3.17 Dubbo 支持分布式事務(wù)嗎剪勿?
目前暫時不支持,可與通過 tcc-transaction 框架實現(xiàn)
tcc-transaction 是開源的 TCC 補(bǔ)償性分布式事務(wù)框架 TCC-Transaction 通過 Dubbo 隱式傳參的功能方庭,避免自己對業(yè)務(wù)代碼的入侵厕吉。
3.18 Dubbo 可以對結(jié)果進(jìn)行緩存嗎?
為了提高數(shù)據(jù)訪問的速度械念。Dubbo 提供了聲明式緩存头朱,以減少用戶加緩存的工作量<dubbo:reference cache="true" /> 其實比普通的配置文件就多了一個標(biāo)簽 cache="true"
3.19 服務(wù)上線怎么兼容舊版本?
可以用版本號(version)過渡龄减,多個不同版本的服務(wù)注冊到注冊中心项钮,版本號不同的服務(wù)相互間不引用。這個和服務(wù)分組的概念有一點(diǎn)類似希停。
3.20 Dubbo 必須依賴的包有哪些烁巫?
Dubbo 必須依賴 JDK,其他為可選宠能。
3.21 Dubbo telnet 命令能做什么程拭?
dubbo 服務(wù)發(fā)布之后,我們可以利用 telnet 命令進(jìn)行調(diào)試棍潘、管理恃鞋。Dubbo2.0.5 以上版本服務(wù)提供端口支持 telnet 命令 連接服務(wù) telnet localhost 20880 //鍵入回車進(jìn)入 Dubbo 命令模式。 查看服務(wù)列表
dubbo>ls
com.test.TestService
dubbo>ls com.test.TestService
create
delete
query
· ls (list services and methods)
· ls : 顯示服務(wù)列表亦歉。
· ls -l : 顯示服務(wù)詳細(xì)信息列表恤浪。
· ls XxxService:顯示服務(wù)的方法列表。
· ls -l XxxService:顯示服務(wù)的方法詳細(xì)信息列表肴楷。
3.22 Dubbo 如何優(yōu)雅停機(jī)水由?
Dubbo 是通過 JDK 的 ShutdownHook 來完成優(yōu)雅停機(jī)的,所以如果使用kill -9 PID 等強(qiáng)制關(guān)閉指令赛蔫,是不會執(zhí)行優(yōu)雅停機(jī)的砂客,只有通過 kill PID 時泥张,才會執(zhí)行。
3.23 Dubbo 和 Dubbox 之間的區(qū)別鞠值?
Dubbox 是繼 Dubbo 停止維護(hù)后媚创,當(dāng)當(dāng)網(wǎng)基于 Dubbo 做的一個擴(kuò)展項目,如加了服務(wù)可 Restful 調(diào)用彤恶,更新了開源組件等钞钙。
3.24 Dubbo和SpringCloud的區(qū)別?springcloud和Dubbo的對比声离?你覺得用 Dubbo 好還是 Spring Cloud 好芒炼?
1)通信方式不同
Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式术徊。 由于Dubbo底層是使用Netty這樣的NIO框架本刽,是基于TCP協(xié)議傳輸?shù)模浜弦訦ession序列化完成RPC赠涮。 SpringCloud是基于Http協(xié)議+rest接口調(diào)用遠(yuǎn)程過程的盅安,相對來說,Http請求會有更大的報文世囊,占的帶寬也會更多别瞭。
2)組成部分不同
3)社區(qū)活躍度
Dubbo的更新數(shù)度慢,更新頻率低 相比于 Dubbo株憾,spring Cloud是一個正在持續(xù)維護(hù)的蝙寨、社區(qū)更加火熱的開源項目,這就可以保證使用它構(gòu)建的系統(tǒng)持續(xù)地得到開源力量的支持嗤瞎。
3.25 Dubbo啟動時如果依賴的服務(wù)不可用會怎樣墙歪?
Dubbo缺省會在啟動時檢查依賴的服務(wù)是否可用,不可用時會拋出異常贝奇,阻止Spring初始化完成虹菲,默認(rèn)check=“true”,可以通過 check=“false” 關(guān)閉檢查掉瞳。
3.26 Dubbo的重試機(jī)制
Dubbo在調(diào)用服務(wù)不成功時毕源,默認(rèn)會重試2次。加上初始一次陕习,總共調(diào)用提供者3次 Dubbo的路由機(jī)制霎褐,會把超時的請求路由到其他機(jī)器上,而不是本次嘗試该镣,所以Dubbo的重試機(jī)制也能一定程度的保證服務(wù)的治理冻璃。 如果不合理的配置重試次數(shù),當(dāng)失敗時會進(jìn)行重試多次,這樣在某個時間點(diǎn)出現(xiàn)性能問題省艳,調(diào)用方再連續(xù)重復(fù)調(diào)用娘纷,系統(tǒng)請求變?yōu)檎V档膔etries倍,系統(tǒng)壓力會大增跋炕,容易引起服務(wù)雪崩赖晶,需要根據(jù)業(yè)務(wù)情況規(guī)劃好如何進(jìn)行異常處理,何時進(jìn)行重試枣购。
<dubbo:reference id="xx" interface="xx" retries="2" timeout="3000"/>
timeout=“3000” 嬉探,服務(wù)調(diào)用的超時時間擦耀,調(diào)用服務(wù)的過程中如果達(dá)到3秒就會報超時異常棉圈,超時異常后客戶端會進(jìn)行嘗試設(shè)定的“retries”次調(diào)用。有一個需要注意的地方眷蜓,timeout只有在超時異常才有效分瘾,如果是其他異常導(dǎo)致dubbo服務(wù)調(diào)用拋異常,會立即進(jìn)入下一次嘗試吁系。
retries=“2” 德召,即重試兩次,如果失敗就拋出異常汽纤。
3.27 Dubbo有哪幾種負(fù)載均衡策略上岗,默認(rèn)是哪種?Dubbo的負(fù)載均衡原理蕴坪?
Random LoadBalance:隨機(jī)肴掷,按權(quán)重設(shè)置隨機(jī)概率。默認(rèn) RoundRobin LoadBalance:輪詢背传,按公約后的權(quán)重設(shè)置輪詢比率呆瞻。 LeastActive LoadBalance:最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機(jī)径玖。 ConsistentHash LoadBalance:一致性Hash,相同參數(shù)的請求總是發(fā)到同一提供者痴脾。
3.28 底層是怎么實現(xiàn)的?
Dubb的底層通信是利用netty來實現(xiàn)的梳星。
3.29 Dubbo的線程模型
Dubbo線程模型包括線程模型策略和Dubbo線程池策略兩個方面赞赖。
線程模型策略
Dubbo默認(rèn)的底層網(wǎng)絡(luò)通信使用的是Netty,服務(wù)提供方NettyServer使用兩級線程池冤灾,其中EventLoopGroup(boss)主要用來接收客戶端的鏈接請求薯定,并把完成TCP三次握手的連接分發(fā)給EventLoopGroup(worker)來處理,注意把boss和worker線程組稱為I/O線程瞳购,前者處理IO連接事件话侄,后者處理IO讀寫事件。
Dubbo中根據(jù)請求的消息類是直接被I/O線程處理還是被業(yè)務(wù)線程池處理,Dubbo提供了下面幾種線程模型:
all(AllDispatcher類):所有消息都派發(fā)到業(yè)務(wù)線程池年堆,這些消息包括請求吞杭、響應(yīng)、連接事件变丧、斷開事件等芽狗,響應(yīng)消息會優(yōu)先使用對于請求所使用的線程池。默認(rèn)的策略
direct(DirectDispatcher類):所有消息都不派發(fā)到業(yè)務(wù)線程池痒蓬,全部在IO線程上直接執(zhí)行童擎。
message(MessageOnlyDispatcher類):只有請求響應(yīng)消息派發(fā)到業(yè)務(wù)線程池,其他消息如連接事件攻晒、斷開事件顾复、心跳事件等,直接在I/O線程上執(zhí)行鲁捏。
execution(ExecutionDispatcher類):只把請求類消息派發(fā)到業(yè)務(wù)線程池處理芯砸,但是響應(yīng)、連接事件给梅、斷開事件假丧、心跳事件等消息直接在I/O線程上執(zhí)行。
connection(ConnectionOrderedDispatcher類):在I/O線程上將連接事件动羽、斷開事件放入隊列包帚,有序地逐個執(zhí)行,其他消息派發(fā)到業(yè)務(wù)線程池處理运吓。
AllChannelHandler是默認(rèn)的策略:
下面以DirectDispatcher類進(jìn)行分析:
public class DirectChannelHandler extends WrappedChannelHandler {
public DirectChannelHandler(ChannelHandler handler, URL url) {
super(handler, url);
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
ExecutorService executor = getPreferredExecutorService(message);
if (executor instanceof ThreadlessExecutor) {
try {
executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
}
} else {
handler.received(channel, message);
}
}
}
DirectDispatcher類重寫了received方法渴邦,注意 ThreadlessExecutor 被應(yīng)用在調(diào)用 future.get() 之前,先調(diào)用 ThreadlessExecutor.wait()羽德,wait 會使業(yè)務(wù)線程在一個阻塞隊列上等待几莽,直到隊列中被加入元素。很明顯宅静,provider側(cè)調(diào)用getPreferredExecutorService(message)返回的不是ThreadlessExecutor章蚣,所以會在當(dāng)前IO線程執(zhí)行執(zhí)行。
其他事件姨夹,比如連接纤垂、異常、斷開等磷账,都是在WrappedChannelHandler中默認(rèn)實現(xiàn):執(zhí)行在當(dāng)前IO線程中執(zhí)行的峭沦,代碼如下:
@Override
public void connected(Channel channel) throws RemotingException {
handler.connected(channel);
}
@Override
public void disconnected(Channel channel) throws RemotingException {
handler.disconnected(channel);
}
@Override
public void sent(Channel channel, Object message) throws RemotingException {
handler.sent(channel, message);
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
handler.received(channel, message);
}
@Override
public void caught(Channel channel, Throwable exception) throws RemotingException {
handler.caught(channel, exception);
}
Dubbo線程池策略
Dubbo處理流程,為了盡量早地釋放Netty的I/O線程逃糟,某些線程模型會把請求投遞到線程池進(jìn)行異步處理吼鱼,那么這里所謂的線程池是什么樣的線程池呢蓬豁?
其實這里的線程池ThreadPool也是一個擴(kuò)展接口SPI,Dubbo提供了該擴(kuò)展接口的一些實現(xiàn)菇肃,具體如下:
FixedThreadPool:創(chuàng)建一個具有固定個數(shù)線程的線程池地粪。默認(rèn)的線程池策略
LimitedThreadPool:創(chuàng)建一個線程池,這個線程池中的線程個數(shù)隨著需要量動態(tài)增加琐谤,但是數(shù)量不超過配置的閾值蟆技。另外,空閑線程不會被回收斗忌,會一直存在质礼。
EagerThreadPool:創(chuàng)建一個線程池,在這個線程池中织阳,當(dāng)所有核心線程都處于忙碌狀態(tài)時眶蕉,將創(chuàng)建新的線程來執(zhí)行新任務(wù),而不是把任務(wù)放入線程池阻塞隊列陈哑。
CachedThreadPool:創(chuàng)建一個自適應(yīng)線程池妻坝,當(dāng)線程空閑1分鐘時伸眶,線程會被回收惊窖;當(dāng)有新請求到來時,會創(chuàng)建新線程厘贼。
知道了這些線程池之后界酒,那么是什么時候進(jìn)行SPI加載對應(yīng)的線程池實現(xiàn)呢?具體是在Dubbo 線程模型獲取對應(yīng)線程池時進(jìn)行SPI加載的嘴秸,具體邏輯在方法 org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository#createExecutor中:
private ExecutorService createExecutor(URL url) {
return (ExecutorService) extensionAccessor.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
}
@SPI(value = "fixed", scope = ExtensionScope.FRAMEWORK)
public interface ThreadPool {
/**
* Thread pool
*
* @param url URL contains thread parameter
* @return thread pool
*/
@Adaptive({THREADPOOL_KEY})
Executor getExecutor(URL url);
}
從代碼來看毁欣,默認(rèn)的線程池策略是fixed模式的線程池,其coreSize默認(rèn)為200岳掐,隊列大小為0凭疮,其代碼如下:
/**
* Creates a thread pool that reuses a fixed number of threads
*
* @see java.util.concurrent.Executors#newFixedThreadPool(int)
*/
public class FixedThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME));
int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
}
3.30 Dubbo 超時時間怎樣設(shè)置?
Dubbo 超時時間設(shè)置有兩種方式:
服務(wù)提供者端設(shè)置超時時間,在 Dubbo 的用戶文檔中串述,推薦如果能在服務(wù) 端多配置就盡量多配置执解,因為服務(wù)提供者比消費(fèi)者更清楚自己提供的服務(wù)特性。
服務(wù)消費(fèi)者端設(shè)置超時時間纲酗,如果在消費(fèi)者端設(shè)置了超時時間衰腌,以消費(fèi)者端 為主,即優(yōu)先級更高觅赊。因為服務(wù)調(diào)用方設(shè)置超時時間控制性更靈活右蕊。如果消 費(fèi)方超時,服務(wù)端線程不會定制吮螺,會產(chǎn)生警告饶囚。
3.31 RPC vs REST
服務(wù)提供方與調(diào)用方接口依賴方式太強(qiáng):我們?yōu)槊總€微服務(wù)定義了各自的service抽象接口帕翻,并通過持續(xù)集成發(fā)布到私有倉庫中,調(diào)用方應(yīng)用對微服務(wù)提供的抽象接口存在強(qiáng)依賴關(guān)系萝风,因此不論開發(fā)熊咽、測試、集成環(huán)境都需要嚴(yán)格的管理版本依賴闹丐,才不會出現(xiàn)服務(wù)方與調(diào)用方的不一致導(dǎo)致應(yīng)用無法編譯成功等一系列問題横殴,以及這也會直接影響本地開發(fā)的環(huán)境要求,往往一個依賴很多服務(wù)的上層應(yīng)用卿拴,每天都要更新很多代碼并install之后才能進(jìn)行后續(xù)的開發(fā)衫仑。若沒有嚴(yán)格的版本管理制度或開發(fā)一些自動化工具,這樣的依賴關(guān)系會成為開發(fā)團(tuán)隊的一大噩夢堕花。而REST接口相比RPC更為輕量化文狱,服務(wù)提供方和調(diào)用方的依賴只是依靠一紙契約,不存在代碼級別的強(qiáng)依賴缘挽,當(dāng)然REST接口也有痛點(diǎn)瞄崇,因為接口定義過輕,很容易導(dǎo)致定義文檔與實際實現(xiàn)不一致導(dǎo)致服務(wù)集成時的問題壕曼,但是該問題很好解決苏研,只需要通過每個服務(wù)整合swagger,讓每個服務(wù)的代碼與文檔一體化腮郊,就能解決摹蘑。所以在分布式環(huán)境下,REST方式的服務(wù)依賴要比RPC方式的依賴更為靈活轧飞。
服務(wù)對平臺敏感衅鹿,難以簡單復(fù)用:通常我們在提供對外服務(wù)時,都會以REST的方式提供出去过咬,這樣可以實現(xiàn)跨平臺的特點(diǎn)大渤,任何一個語言的調(diào)用方都可以根據(jù)接口定義來實現(xiàn)。那么在Dubbo中我們要提供REST接口時掸绞,不得不實現(xiàn)一層代理泵三,用來將RPC接口轉(zhuǎn)換成REST接口進(jìn)行對外發(fā)布。若我們每個服務(wù)本身就以REST接口方式存在集漾,當(dāng)要對外提供服務(wù)時切黔,主要在API網(wǎng)關(guān)中配置映射關(guān)系和權(quán)限控制就可實現(xiàn)服務(wù)的復(fù)用了。
Dubbo實現(xiàn)了服務(wù)治理的基礎(chǔ)具篇,但是要完成一個完備的微服務(wù)架構(gòu)纬霞,還需要在各環(huán)節(jié)去擴(kuò)展和完善以保證集群的健康,以減輕開發(fā)驱显、測試以及運(yùn)維各個環(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ù)必在各個環(huán)節(jié)做好整套解決方案的準(zhǔn)備销凑,不然很可能隨著服務(wù)數(shù)量的增長丛晌,整個團(tuán)隊都將疲于應(yīng)付各種架構(gòu)上不足引起的困難。而如果選擇Spring Cloud斗幼,相對來說每個環(huán)節(jié)都已經(jīng)有了對應(yīng)的組件支持澎蛛,可能有些也不一定能滿足你所有的需求,但是其活躍的社區(qū)與高速的迭代進(jìn)度也會是你可以依靠的強(qiáng)大后盾蜕窿。
Dubbox: 相對于Dubbo支持了REST谋逻,估計是很多公司選擇Dubbox的一個重要原因之一,但如果使用Dubbo的RPC調(diào)用方式桐经,服務(wù)間仍然會存在API強(qiáng)依賴毁兆,各有利弊,懂的取舍吧次询。