微服務(wù)面試必問(wèn)的Dubbo,這么詳細(xì)還怕自己找不到工作?
大家好,我是小羽。
Dubbo 起源于阿里巴巴,對(duì)于我們做電商開(kāi)發(fā)的人來(lái)說(shuō),基本是首選的技術(shù),那么為何一個(gè)區(qū)區(qū) soa 服務(wù)治理框架,會(huì)受到這么多人的青睞呢?
今天就跟著小羽一起看看這個(gè)微服務(wù)框架之一的 Dubbo 的詳細(xì)解讀吧。
前言
互聯(lián)網(wǎng)的不斷發(fā)展,網(wǎng)站應(yīng)用的規(guī)模不斷擴(kuò)大,常規(guī)的垂直應(yīng)用架構(gòu)已無(wú)法應(yīng)對(duì)。
服務(wù)化的進(jìn)一步發(fā)展,服務(wù)越來(lái)越多,服務(wù)之間的調(diào)用和依賴(lài)關(guān)系也越來(lái)越復(fù)雜,誕生了面向服務(wù)的架構(gòu)體系(SOA),
也因此衍生出了一系列相應(yīng)的技術(shù),如對(duì)服務(wù)提供、服務(wù)調(diào)用、連接處理、通信協(xié)議、序列化方式、服務(wù)發(fā)現(xiàn)、服務(wù)路由、日志輸出等行為進(jìn)行封裝的服務(wù)框架。
就這樣分布式系統(tǒng)的服務(wù)治理框架就出現(xiàn)了,Dubbo也就這樣產(chǎn)生了。
概念
Dubbo 是一款高性能、輕量級(jí)的開(kāi)源 RPC 框架、提供服務(wù)自動(dòng)注冊(cè)、自動(dòng)發(fā)現(xiàn)等高效治理方案,可以和 Spring 框架無(wú)縫集成。
簡(jiǎn)單的說(shuō),dubbo就是個(gè)分布式服務(wù)框架,在有分布式需要的時(shí)候可以使用 dubbo 的框架,使用 dubbo 的好處:
1、透明化的遠(yuǎn)程方法調(diào)用
2、軟負(fù)載均衡及容錯(cuò)機(jī)制
3、服務(wù)自動(dòng)注冊(cè)與發(fā)現(xiàn)
4、提供了完善的服務(wù)接口管理與監(jiān)控功能

RPC
簡(jiǎn)介
RPC 全稱(chēng)為 remote procedure call,即遠(yuǎn)程過(guò)程調(diào)用。比如兩臺(tái)服務(wù)器 A 和 B,A 服務(wù)器上部署一個(gè)應(yīng)用,B 服務(wù)器上部署一個(gè)應(yīng)用,A 服務(wù)器上的應(yīng)用想調(diào)用 B 服務(wù)器上的應(yīng)用提供的方法,由于兩個(gè)應(yīng)用不在一個(gè)內(nèi)存空間,不能直接調(diào)用,所以需要通過(guò)網(wǎng)絡(luò)來(lái)表達(dá)調(diào)用的語(yǔ)義和傳達(dá)調(diào)用的數(shù)據(jù)。
RPC 并不是一個(gè)具體的技術(shù),而是指整個(gè)網(wǎng)絡(luò)遠(yuǎn)程調(diào)用過(guò)程。
RPC 是一個(gè)泛化的概念,嚴(yán)格來(lái)說(shuō)一切遠(yuǎn)程過(guò)程調(diào)用手段都屬于 RP C范疇。各種開(kāi)發(fā)語(yǔ)言都有自己的 RPC 框架。Java 中的 RPC 框架比較多,廣泛使用的有 RMI、Hessian、Dubbo 等。
原理
服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù)??蛻?hù)端存根(client stub)接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等編碼成能在網(wǎng)絡(luò)中傳輸?shù)南Ⅲw。然后,客戶(hù)端存根找到服務(wù)地址后,將消息發(fā)送給服務(wù)端。
服務(wù)提供方(server)收到序列化后的消息,就按照解碼該消息。然后,根據(jù)解碼結(jié)果調(diào)用本地服務(wù),執(zhí)行完畢后,將結(jié)果打包發(fā)送給消費(fèi)方。
服務(wù)消費(fèi)方收到執(zhí)行結(jié)果后,也是進(jìn)行解碼后得到結(jié)果。

使用場(chǎng)景
RPC 分布式服務(wù),拆分應(yīng)用進(jìn)行服務(wù)化,提高開(kāi)發(fā)效率,調(diào)優(yōu)性能,節(jié)省競(jìng)爭(zhēng)資源
配置管理,解決服務(wù)的地址信息劇增,配置困難的問(wèn)題
服務(wù)依賴(lài),解決服務(wù)間依賴(lài)關(guān)系錯(cuò)蹤復(fù)雜的問(wèn)題
服務(wù)擴(kuò)容,解決隨著訪問(wèn)量的不斷增大,動(dòng)態(tài)擴(kuò)展服務(wù)提供方的機(jī)器的問(wèn)題
核心功能
Remoting:遠(yuǎn)程通訊,提供對(duì)多種 NIO 框架抽象封裝,包括“同步轉(zhuǎn)異步”和“請(qǐng)求-響應(yīng)”模式的信息交換方式。
Cluster:服務(wù)框架,提供基于接口方法的透明遠(yuǎn)程過(guò)程調(diào)用,包括多協(xié)議支持,以及軟負(fù)載均衡,失敗容錯(cuò),地址路由,動(dòng)態(tài)配置等集群支持。
Registry:服務(wù)注冊(cè)中心,服務(wù)自動(dòng)發(fā)現(xiàn): 基于注冊(cè)中心目錄服務(wù),使服務(wù)消費(fèi)方能動(dòng)態(tài)的查找服務(wù)提供方,使地址透明,使服務(wù)提供方可以平滑增加或減少機(jī)器。
核心組件
Provider:服務(wù)的提供方
Consumer:調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方
Registry:服務(wù)注冊(cè)和發(fā)現(xiàn)的注冊(cè)中心
Monitor:統(tǒng)計(jì)服務(wù)調(diào)用次數(shù)和調(diào)用時(shí)間的監(jiān)控中心
Container:服務(wù)運(yùn)行容器

服務(wù)注冊(cè)與發(fā)現(xiàn)
流程如下:
1、Provider(提供者)綁定指定端口并啟動(dòng)服務(wù)
2、供者連接注冊(cè)中心,并發(fā)本機(jī) IP、端口、應(yīng)用信息和提供服務(wù)信息發(fā)送至注冊(cè)中心存儲(chǔ)
3、Consumer(消費(fèi)者),連接注冊(cè)中心 ,并發(fā)送應(yīng)用信息、所求服務(wù)信息至注冊(cè)中心
4、注冊(cè)中心根據(jù)消費(fèi)者所求服務(wù)信息匹配對(duì)應(yīng)的提供者列表發(fā)送至Consumer 應(yīng)用緩存。
5、Consumer 在發(fā)起遠(yuǎn)程調(diào)用時(shí)基于緩存的消費(fèi)者列表?yè)衿湟话l(fā)起調(diào)用。
6、Provider 狀態(tài)變更會(huì)實(shí)時(shí)通知注冊(cè)中心、在由注冊(cè)中心實(shí)時(shí)推送至Consumer設(shè)計(jì)的原因:
Consumer 與 Provider 解偶,雙方都可以橫向增減節(jié)點(diǎn)數(shù)。注冊(cè)中心對(duì)本身可做對(duì)等集群,可動(dòng)態(tài)增減節(jié)點(diǎn),并且任意一臺(tái)宕掉后,將自動(dòng)切換到另一臺(tái)
7、去中心化,雙方不直接依懶注冊(cè)中心,即使注冊(cè)中心全部宕機(jī)短時(shí)間內(nèi)也不會(huì)影響服務(wù)的調(diào)用
8、服務(wù)提供者無(wú)狀態(tài),任意一臺(tái)宕掉后,不影響使用

服務(wù)治理
治理原因
Dubbo的服務(wù)治理主要原因:
1、過(guò)多的服務(wù) URL 配置困難。
2、負(fù)載均衡分配節(jié)點(diǎn)壓力過(guò)大的情況下也需要部署集群。
3、服務(wù)依賴(lài)混亂,啟動(dòng)順序不清晰。
4、過(guò)多服務(wù)導(dǎo)致性能指標(biāo)分析難度較大,需要監(jiān)控。
主要特性
透明遠(yuǎn)程調(diào)用:就像調(diào)用本地方法一樣調(diào)用遠(yuǎn)程方法;只需簡(jiǎn)單配置,沒(méi)有任何 API 侵入
負(fù)載均衡機(jī)制:Client 端 LB,可在內(nèi)網(wǎng)替代 F5 等硬件負(fù)載均衡器
容錯(cuò)重試機(jī)制:服務(wù) Mock 數(shù)據(jù),重試次數(shù)、超時(shí)機(jī)制等
自動(dòng)注冊(cè)發(fā)現(xiàn):注冊(cè)中心基于接口名查詢(xún)服務(wù)提 供者的 IP 地址,并且能夠平滑添加或刪除服務(wù)提供者
性能日志監(jiān)控:Monitor 統(tǒng)計(jì)服務(wù)的調(diào)用次調(diào)和調(diào)用時(shí)間的監(jiān)控中心
服務(wù)治理中心:路由規(guī)則,動(dòng)態(tài)配置,服務(wù)降級(jí),訪問(wèn)控制,權(quán)重調(diào)整,負(fù)載均衡,等手動(dòng)配置
自動(dòng)治理中心:無(wú),比如:熔斷限流機(jī)制、自動(dòng)權(quán)重調(diào)整等(因此可以搭配SpringCloud的熔斷機(jī)制等進(jìn)行開(kāi)發(fā))

架構(gòu)設(shè)計(jì)
整體架構(gòu)
先看下 Dubbo 的整體架構(gòu)圖:
圖例說(shuō)明:

圖中左邊淡藍(lán)背景的為服務(wù)消費(fèi)方使用的接口,右邊淡綠色背景的為服務(wù)提供方使用的接口,位于中軸線上的為雙方都用到的接口。
圖中從下至上分為十層,各層均為單向依賴(lài),右邊的黑色箭頭代表層之間的依賴(lài)關(guān)系,每一層都可以剝離上層被復(fù)用,其中,Service 和 Config 層為 API,其它各層均為 SPI。
圖中綠色小塊的為擴(kuò)展接口,藍(lán)色小塊為實(shí)現(xiàn)類(lèi),圖中只顯示用于關(guān)聯(lián)各層的實(shí)現(xiàn)類(lèi)。
圖中藍(lán)色虛線為初始化過(guò)程,即啟動(dòng)時(shí)組裝鏈,紅色實(shí)線為方法調(diào)用過(guò)程,即運(yùn)行時(shí)調(diào)時(shí)鏈,紫色三角箭頭為繼承,可以把子類(lèi)看作父類(lèi)的同一個(gè)節(jié)點(diǎn),線上的文字為調(diào)用的方法。
各層說(shuō)明
config 配置層:對(duì)外配置接口,以 ServiceConfig, ReferenceConfig 為中心,可以直接初始化配置類(lèi),也可以通過(guò) spring 解析配置生成配置類(lèi)
proxy 服務(wù)代理層:服務(wù)接口透明代理,生成服務(wù)的客戶(hù)端 Stub 和服務(wù)器端 Skeleton,以ServiceProxy 為中心,擴(kuò)展接口為 ProxyFactory
registry 注冊(cè)中心層:封裝服務(wù)地址的注冊(cè)與發(fā)現(xiàn),以服務(wù) URL 為中心,擴(kuò)展接口為RegistryFactory, Registry, RegistryService
cluster 路由層:封裝多個(gè)提供者的路由及負(fù)載均衡,并橋接注冊(cè)中心,以 Invoker 為中心,擴(kuò)展接口為 Cluster, Directory, Router, LoadBalance
monitor 監(jiān)控層:RPC 調(diào)用次數(shù)和調(diào)用時(shí)間監(jiān)控,以 Statistics 為中心,擴(kuò)展接口為MonitorFactory, Monitor, MonitorService
protocol 遠(yuǎn)程調(diào)用層:封裝 RPC 調(diào)用,以 Invocation, Result 為中心,擴(kuò)展接口為 Protocol, Invoker, Exporter
exchange 信息交換層:封裝請(qǐng)求響應(yīng)模式,同步轉(zhuǎn)異步,以 Request, Response 為中心,擴(kuò)展接口為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport 網(wǎng)絡(luò)傳輸層:抽象 mina 和 netty 為統(tǒng)一接口,以 Message 為中心,擴(kuò)展接口為 Channel, Transporter, Client, Server, Codec
serialize 數(shù)據(jù)序列化層:可復(fù)用的一些工具,擴(kuò)展接口為 Serialization, ObjectInput, ObjectOutput, ThreadPool
主要模塊
dubbo-common 公共邏輯模塊,包括 Util 類(lèi)和通用模型。
dubbo-remoting 遠(yuǎn)程通訊模塊,相當(dāng)于 Dubbo 協(xié)議的實(shí)現(xiàn),如果 RPC 用 RMI 協(xié)議則不需要使用此包。
dubbo-rpc 遠(yuǎn)程調(diào)用模塊,抽象各種協(xié)議,以及動(dòng)態(tài)代理,只包含一對(duì)一的調(diào)用,不關(guān)心集群的管理。
dubbo-cluster 集群模塊,將多個(gè)服務(wù)提供方偽裝為一個(gè)提供方,包括:負(fù)載均衡、容錯(cuò)、路由等,集群的地址列表可以是靜態(tài)配置的,也可以是由注冊(cè)中心下發(fā)。
dubbo-registry 注冊(cè)中心模塊,基于注冊(cè)中心下發(fā)地址的集群方式,以及對(duì)各種注冊(cè)中心的抽象。
dubbo-monitor 監(jiān)控模塊,統(tǒng)計(jì)服務(wù)調(diào)用次數(shù),調(diào)用時(shí)間的,調(diào)用鏈跟蹤的服務(wù)。
dubbo-config 配置模塊,是 Dubbo 對(duì)外的 API ,用戶(hù)通過(guò) Config 使用 Dubbo ,隱藏 Dubbo 所有細(xì)節(jié)。
dubbo-container 容器模塊,是一個(gè) Standalone 的容器,以簡(jiǎn)單的 Main 加載 Spring 啟動(dòng),因?yàn)榉?wù)通常不需要 Tomcat/JBoss 等 Web 容器的特性,沒(méi)必要用 Web 容器去加載服務(wù)。

調(diào)用方式
異步調(diào)用
基于 NIO 的非阻塞實(shí)現(xiàn)并行調(diào)用,客戶(hù)端不需要啟動(dòng)多線程即可完成并行調(diào)用多個(gè)遠(yuǎn)程服務(wù),相對(duì)多線程開(kāi)銷(xiāo)較小

本地調(diào)用
使用了Injvm協(xié)議,是一個(gè)偽協(xié)議,它不開(kāi)啟端口,不發(fā)起遠(yuǎn)程調(diào)用,只在JVM內(nèi)直接關(guān)聯(lián),但執(zhí)行Dubbo的Filter鏈。
Define injvm protocol:
<dubbo:protocol name="injvm" /> Set default protocol:
<dubbo:provider protocol="injvm" />
Set service protocol:
<dubbo:service protocol="injvm" />Use injvm first:(服務(wù)暴露與服務(wù)引用都需要聲明injvm=“true”)
<dubbo:consumer injvm="true" .../>
<dubbo:provider injvm="true" .../>
或
<dubbo:reference injvm="true" .../> <dubbo:service injvm="true" .../>容錯(cuò)機(jī)制
調(diào)用流程
1、Cluster 將 Directory 中的多個(gè) Invoker 偽裝成一個(gè)Invoker,對(duì)上層透明,偽裝過(guò)程包含了容錯(cuò)邏輯
2、Router 負(fù)責(zé)從多個(gè) Invoker 中按路由規(guī)則選出子集,比如讀寫(xiě)分離,應(yīng)用隔離等
3、LoadBalance 負(fù)責(zé)從多個(gè) Invoker 中選出具體的一個(gè)用于本次調(diào)用,選的過(guò)程包含了負(fù)載均衡算法

容錯(cuò)策略
Dubbo 官網(wǎng)提出總共有六種容錯(cuò)策略
1、Failover Cluster
失敗自動(dòng)切換,當(dāng)出現(xiàn)失敗,重試其它服務(wù)器。(默認(rèn))
2、Failfast Cluster
快速失敗,只發(fā)起一次調(diào)用,失敗立即報(bào)錯(cuò)。通常用于非冪等性的寫(xiě)操作,比如新增記錄。
3、Failsafe Cluster
失敗安全,出現(xiàn)異常時(shí),直接忽略。通常用于寫(xiě)入審計(jì)日志等操作。
4、Failback Cluster
失敗自動(dòng)恢復(fù),后臺(tái)記錄失敗請(qǐng)求,定時(shí)重發(fā)。通常用于消息通知操作。
5、Forking Cluster
并行調(diào)用多個(gè)服務(wù)器,只要一個(gè)成功即返回。通常用于實(shí)時(shí)性要求較高的讀操作,但需要浪費(fèi)更多服務(wù)資源。
可通過(guò) forks=”2”來(lái)設(shè)置最大并行數(shù)。
6、Broadcast Cluster
廣播調(diào)用所有提供者,逐個(gè)調(diào)用,任意一臺(tái)報(bào)錯(cuò)則報(bào)錯(cuò)。(2.1.0 開(kāi)始支持) 通常用于通知所有提供者更新緩存或日志等本地資源信息。
總結(jié):在實(shí)際應(yīng)用中查詢(xún)語(yǔ)句容錯(cuò)策略建議使用默認(rèn) Failover Cluster,而增刪改建議使用 Failfast Cluster 或者使用 Failover Cluster(retries=”0”)策略,防止出現(xiàn)數(shù)據(jù)重復(fù)添加等等其它問(wèn)題!建議在設(shè)計(jì)接口時(shí)候把查詢(xún)接口方法單獨(dú)做一個(gè)接口提供查詢(xún)。
連接方式
Dubbo 的客戶(hù)端和服務(wù)端有三種連接方式,分別是:廣播、直連和使用Zookeeper注冊(cè)中心。
Dubbo 廣播
這種方式是dubbo官方入門(mén)程序所使用的連接方式,但是這種方式有很多問(wèn)題,在企業(yè)開(kāi)發(fā)中不使用廣播的方式。
服務(wù)端配置:
<!--配制dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-service"/>
<!--使用multicast廣播注冊(cè)暴露服務(wù)地址-->
<dubbo:registry address="multicast://192.168.9.4:88888" />
<!--使用dubbo協(xié)議在20880端口暴露服務(wù)-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--聲明暴露的服務(wù)接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />
客戶(hù)端配置:
<!--配合dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-web"/>
<!--使用multicast廣播注冊(cè)中心暴露服務(wù)地址 -->
<dubbo:registry address="multicast://19.188.8.9:8888"/>
<!--聲明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />
Dubbo 直連
這種方式在企業(yè)中一般在開(kāi)發(fā)中環(huán)境中使用,但是生產(chǎn)環(huán)境很少使用,因?yàn)榉?wù)是直接調(diào)用,沒(méi)有使用注冊(cè)中心,很難對(duì)服務(wù)進(jìn)行管理。Dubbo 直連,首先要取消廣播,然后客戶(hù)端直接到指定需要的服務(wù)的 url 獲取服務(wù)即可。
服務(wù)端配置:
<!--配制dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-service"/>
<!--使用multicast廣播注冊(cè)暴露服務(wù)地址-->
<-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->
<dubbo:registry adress="N/A">
<!--使用dubbo協(xié)議在20880端口暴露服務(wù)-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--聲明暴露的服務(wù)接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />
客戶(hù)端配置:
<!--配合dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-web"/>
<!--使用multicast廣播注冊(cè)中心暴露服務(wù)地址 -->
<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->
<!--聲明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" url="dubbo://127.0.0.1:20880" />
zookeeper 注冊(cè)中心
Dubbo 注冊(cè)中心和廣播注冊(cè)中心配置類(lèi)似,不過(guò)需要指定注冊(cè)中心類(lèi)型和注冊(cè)中心地址,這個(gè)時(shí)候就不是把服務(wù)信息進(jìn)行廣播了,而是告訴給注冊(cè)中心進(jìn)行管理,這個(gè)時(shí)候我們就需要有一個(gè)注冊(cè)中心,官方推薦使用 zookeeper 作為注冊(cè)中心。

注冊(cè)中心負(fù)責(zé)服務(wù)地址的注冊(cè)與查找,相當(dāng)于目錄服務(wù),服務(wù)提供者在啟動(dòng)時(shí)與注冊(cè)中心交互,消費(fèi)者不斷的發(fā)起請(qǐng)求獲取服務(wù)信息,注冊(cè)中心不轉(zhuǎn)發(fā)請(qǐng)求,壓力較小
服務(wù)端配置:
<!--配制dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-service"/>
<!--使用multicast廣播注冊(cè)暴露服務(wù)地址-->
<!-- <dubbo:registry address="multicast://192.168.9.4:88888" /> -->
<!--<dubbo:registry adress="N/A"> -->
<dubbo:registry protocol="zookeeper" address="192.168.37,136:2181">
<!--使用dubbo協(xié)議在20880端口暴露服務(wù)-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--聲明暴露的服務(wù)接口-->
<dubbo:service interface="com.demo.manger.service.TestService" ref="testServiceImpl" />
客戶(hù)端配置:
<!--配合dubbo-->
<!--提供應(yīng)用信息,用于計(jì)算依賴(lài)關(guān)系-->
<dubbo:application name="demo-web"/>
<!--使用multicast廣播注冊(cè)中心暴露服務(wù)地址 -->
<-- <dubbo:registry address="multicast://19.188.8.9:8888"/> -->
<dubbo:registry protocol="zookeeper" address="192.168.37.1336:2181"/>
<!--聲明需要暴露的接口-->
<dubbo:reference interface="com.demo.manager.service.TestService" id="testService" timeout="1000000" />策略
負(fù)載均衡策略
1、Random LoadBalance,隨機(jī)(默認(rèn)的負(fù)載均衡策略)
RandomLoadBalance 是加權(quán)隨機(jī)算法的具體實(shí)現(xiàn),可以完全隨機(jī),也可以按權(quán)重設(shè)置隨機(jī)概率。
2、RoundRobin LoadBalance,輪循
可以輪詢(xún)和加權(quán)輪詢(xún)。存在響應(yīng)慢的提供者會(huì)累積請(qǐng)求的問(wèn)題,比如:第二臺(tái)機(jī)器很慢,但沒(méi)掛,當(dāng)請(qǐng)求調(diào)到第二臺(tái)時(shí)就卡在那,久而久之,所有請(qǐng)求都卡在調(diào)到第二臺(tái)上。
3、LeastActive LoadBalance,最少活躍調(diào)用數(shù)
活躍調(diào)用數(shù)越小,表明該服務(wù)提供者效率越高,單位時(shí)間內(nèi)可處理更多的請(qǐng)求。此時(shí)應(yīng)優(yōu)先將請(qǐng)求分配給該服務(wù)提供者。
4、ConsistentHash LoadBalance,一致性 Hash
一致性 Hash 算法,相同參數(shù)的請(qǐng)求一定分發(fā)到一個(gè) provider 上去。provider 掛掉的時(shí)候,會(huì)基于虛擬節(jié)點(diǎn)均勻分配剩余的流量,抖動(dòng)不會(huì)太大。
集群容錯(cuò)策略
1、failover cluster(默認(rèn))
失敗自動(dòng)切換,調(diào)用失敗時(shí),自動(dòng)重試其他機(jī)器。通常用于讀操作,但重試會(huì)帶來(lái)更長(zhǎng)延遲。
2、Failfast Cluster
快速失敗,只發(fā)起一次調(diào)用,失敗立即報(bào)錯(cuò)。通常用于非冪等性的寫(xiě)操作,比如新增記錄。
3、Failsafe Cluster
失敗安全,出現(xiàn)異常時(shí),直接忽略。通常用于寫(xiě)入審計(jì)日志等操作。
4、Failback Cluster
失敗自動(dòng)恢復(fù),后臺(tái)記錄失敗請(qǐng)求,定時(shí)重發(fā)。通常用于消息通知操作。
5、Forking Cluster
并行調(diào)用多個(gè)服務(wù)器,只要一個(gè)成功即返回。通常用于實(shí)時(shí)性要求較高的讀操作,但需要浪費(fèi)更多服務(wù)資源。
動(dòng)態(tài)代理策略
默認(rèn)使用 javassist 動(dòng)態(tài)字節(jié)碼生成,創(chuàng)建代理類(lèi)。也可以通過(guò) spi 擴(kuò)展機(jī)制配置自己的動(dòng)態(tài)代理策略。
集群容錯(cuò)方案
配置說(shuō)明,方案配置方式,優(yōu)先使用消費(fèi)端配置
<!--服務(wù)端配置-->
<dubbo:service cluster="failover"/>
<!--消費(fèi)端配置-->
<dubbo:reference cluster="failover"/>
盡量在只在服務(wù)端進(jìn)行配置
cluster類(lèi)型均為小寫(xiě)
默認(rèn)為FailoverCluster失敗切換方案
集群容錯(cuò)方案support
FailoverCluster(默認(rèn)):失敗切換
場(chǎng)景:調(diào)用失敗后切換其他服務(wù)
配置:
<!--
retries:重試次數(shù),不包括第一次,默認(rèn)2次
-->
<dubbo:service cluster="failover" retries="3"/>
代碼實(shí)現(xiàn)邏輯:
1. 根據(jù)負(fù)載均衡策略選出需要調(diào)用的服務(wù)實(shí)例,排除已調(diào)用的
2. 執(zhí)行選出的實(shí)例,并將其保存到已調(diào)用列表中
3. 執(zhí)行實(shí)例成功即返回
4. 執(zhí)行實(shí)例不成功,為到最大重試次數(shù)則執(zhí)行第一步,否則拋出RpcException異常
FailbackCluster:失敗重試
場(chǎng)景:調(diào)用失敗時(shí)記錄失敗請(qǐng)求,定時(shí)重發(fā)
配置:
<!--
retries:重試次數(shù),不包括第一次,默認(rèn)3次
failbacktasks:定時(shí)器中最大掛起任務(wù)數(shù),默認(rèn)100
-->
<dubbo:service cluster="failback" retries="5" failbacktasks="200"/>
代碼實(shí)現(xiàn)邏輯
1. 根據(jù)負(fù)載均衡策略選出需要調(diào)用的服務(wù)實(shí)例
2. 執(zhí)行選出的實(shí)例
3. 執(zhí)行實(shí)例成功即返回
4. 執(zhí)行異常則創(chuàng)建延時(shí)5秒的定時(shí)任務(wù),并加入時(shí)間輪定時(shí)器,第一次需要進(jìn)行定時(shí)器初始化,分為32個(gè)時(shí)間片,每1秒滾動(dòng)一次,最大掛起任務(wù)默認(rèn)100個(gè),超出最大任務(wù)數(shù)時(shí)拋出RejectedExecutionException異常。
5. 重試執(zhí)行定時(shí)任務(wù),次數(shù)超出最大執(zhí)行次數(shù)停止,并輸出error日志,默認(rèn)為3次。
FailfastCluster:快速失敗
場(chǎng)景:調(diào)用失敗立即報(bào)錯(cuò)
配置:
<dubbo:service cluster="failfast"/>
代碼實(shí)現(xiàn)邏輯
1. 根據(jù)負(fù)載均衡策略選出需要調(diào)用的服務(wù)實(shí)例
2. 執(zhí)行選出的實(shí)例
3. 執(zhí)行實(shí)例成功即返回,失敗拋出RpcException異常
FailsafeCluster:安全失敗
場(chǎng)景:調(diào)用失敗后忽略
配置:
<dubbo:service cluster="failsafe"/>
代碼實(shí)現(xiàn)邏輯
1. 根據(jù)負(fù)載均衡策略選出需要調(diào)用的服務(wù)實(shí)例
2. 執(zhí)行選出的實(shí)例
3. 執(zhí)行實(shí)例成功即返回,失敗輸出error日志,并返RpcResult,視為忽略。
ForkingCluster:并發(fā)處理
場(chǎng)景:并發(fā)調(diào)用指定數(shù)量的服務(wù),一個(gè)成功則返回,對(duì)實(shí)時(shí)性要求高的場(chǎng)景,要求快速返回,需要使用更多服務(wù)器資源。
配置:
<!--
forks:最大并發(fā)數(shù),默認(rèn)2
timeout:并發(fā)返回超時(shí)時(shí)間,默認(rèn)1000ms
-->
<dubbo:service cluster="forking" forks="3" timeout="500"/>
代碼實(shí)現(xiàn)邏輯
1. 根據(jù)負(fù)載均衡策略選出幾個(gè)不同的服務(wù)實(shí)例
2. 并發(fā)執(zhí)行選出的幾個(gè)實(shí)例,并將返回結(jié)果放入堵塞隊(duì)列中
3. 返回堵塞隊(duì)列中的第一個(gè)值,如規(guī)定時(shí)間內(nèi)未獲取到隊(duì)列中的值或獲取到異常值則返回RPC異常。
BroadcastCluster:廣播
場(chǎng)景:廣播方式逐個(gè)調(diào)用服務(wù)提供者,有一個(gè)報(bào)錯(cuò)則返回錯(cuò)誤,多用于通知服務(wù)提供者更新本地資源信息,如緩存,日志等。
配置:
<dubbo:service cluster="broadcast"/>
代碼實(shí)現(xiàn)邏輯
1. 循環(huán)逐個(gè)執(zhí)行所有服務(wù)實(shí)例信息
2. 保存一份返回結(jié)果和異常信息
3. 執(zhí)行完全部實(shí)例后,如異常信息不為空,則拋出異常信息,否則返回最后一個(gè)實(shí)例的結(jié)果。
AvailableCluster:可用服務(wù)
場(chǎng)景:調(diào)用第一個(gè)可用服務(wù)
配置:
<dubbo:service cluster="available"/>
代碼實(shí)現(xiàn)邏輯
1. 循環(huán)所有服務(wù)實(shí)例信息
2. 執(zhí)行第一個(gè)可用的實(shí)例,并返回結(jié)果
3. 如無(wú)可用實(shí)例則返回RpcException異常
MergeableCluster:合并處理
場(chǎng)景:返回合并或疊加處理結(jié)果
配置:
<!--
merger:合并發(fā)放名
timeout:調(diào)用服務(wù)超時(shí)時(shí)間,默認(rèn)1000ms
-->
<dubbo:service cluster="mergeable" merger="true" timeout="500"/>
代碼實(shí)現(xiàn)邏輯
1. 判斷merger,為空、null、0、false、N/A是執(zhí)行第一個(gè)可用服務(wù)并返回結(jié)果,無(wú)可用則執(zhí)行第一個(gè)實(shí)例,并返回結(jié)果。
2. 獲取方法實(shí)例的返回類(lèi)型
3. 異步調(diào)用所有實(shí)例,并將異步結(jié)果Result存儲(chǔ)到結(jié)果集中,返回異常輸出error日志
4. 結(jié)果集為空返回 RpcException,大小為 1時(shí)返回第一個(gè)Result
5. 當(dāng)merger的第一個(gè)字符為“.”時(shí),判斷當(dāng) merger 實(shí)例返回類(lèi)型不為void,且返回類(lèi)型必須是結(jié)果集中第一個(gè)返回類(lèi)型的父類(lèi)型或相同類(lèi)型時(shí),循環(huán)執(zhí)行merger實(shí)例,每一次都傳入上一次的返回結(jié)果,最終返回獲取最后一次結(jié)果,非上述情況時(shí)循環(huán)執(zhí)行merger實(shí)例,返回結(jié)果集中的第一個(gè)結(jié)果。
6. 當(dāng)merger為true或default時(shí)使用Dubbo默認(rèn)合并器,否則使用自定義merger合并器,合并后返回
RegistryAwareCluster:默認(rèn)標(biāo)識(shí)、注冊(cè)標(biāo)識(shí)
場(chǎng)景:調(diào)用注冊(cè)默認(rèn)標(biāo)識(shí)的服務(wù)
配置:
<!--
default:默認(rèn)標(biāo)識(shí)
-->
<dubbo:registry address="zookeeper://xxx..." default="true"/>
<dubbo:service cluster="registryaware"/>
代碼實(shí)現(xiàn)邏輯
1.8 循環(huán)所有服務(wù)實(shí)例信息
2. 執(zhí)行第一個(gè)可用的實(shí)例且default為true的實(shí)例
3. 無(wú)默認(rèn)實(shí)例則執(zhí)行第一個(gè)可用的實(shí)例
4. 無(wú)可用的實(shí)例則拋出RpcException異常
主要配置
配置應(yīng)用信息:
<dubbo:application name=“appName-provider” />
配置注冊(cè)中心相關(guān)信息:
<dubbo:registryid=“zk” protocol=“zookeeper” address=“127.0.0.1:2181” />
配置服務(wù)協(xié)議:
<dubbo:protocol name=“dubbo” port=“20880” threadpool=“cached” threads=“80” />
配置所有暴露服務(wù)缺省值:
<dubbo:provider registry=“zk” protocol=“dubbo” retries=“0” version=“1.0.0” timeout=“3000” threadpool=“cached” threads=“4”/>
配置暴露服務(wù):
<dubbo:service interface=“com.orgname.app.serviceX” ref=“serviceX” />
配置所有引用服務(wù)缺省值:
<dubbo:consumer check=“false” timeout=“1000” version=“1.0” retries=“0” async=“false” />
注解配置:
com.alibaba.dubbo.config.annotation.Service 配置暴露服務(wù)
com.alibaba.dubbo.config.annotation.Reference配置引用服務(wù)
超時(shí)設(shè)置
Dubbo消費(fèi)端
全局超時(shí)配置
<dubbo:consumer timeout="5000" />
指定接口以及特定方法超時(shí)配置
<dubbo:reference interface="com.foo.BarService" timeout="2000">
<dubbo:method name="sayHello" timeout="3000" />
</dubbo:reference>
Dubbo服務(wù)端
全局超時(shí)配置
<dubbo:provider timeout="5000" />
指定接口以及特定方法超時(shí)配置
<dubbo:provider interface="com.foo.BarService" timeout="2000">
<dubbo:method name="sayHello" timeout="3000" />
</dubbo:provider>
支持協(xié)議
1、Dubbo 協(xié)議(官方推薦協(xié)議)
優(yōu)點(diǎn):采用NIO復(fù)用單一長(zhǎng)連接,并使用線程池并發(fā)處理請(qǐng)求,減少握手和加大并發(fā)效率,性能較好(推薦使用)
缺點(diǎn):大文件上傳時(shí),可能出現(xiàn)問(wèn)題(不使用 Dubbo 文件上傳)
2、RMI(Remote Method Invocation)協(xié)議
優(yōu)點(diǎn):JDK 自帶的能力。可與原生 RMI 互操作,基于 TCP 協(xié)議
缺點(diǎn):偶爾連接失敗.
3、Hessian協(xié)議
優(yōu)點(diǎn):可與原生 Hessian 互操作,基于 HTTP 協(xié)議
缺點(diǎn):需 hessian.jar 支持,http 短連接的*開(kāi)銷(xiāo)大8
常用設(shè)計(jì)模式
Dubbo 框架在初始化和通信過(guò)程中使用了多種設(shè)計(jì)模式,可靈活控制類(lèi)加載、權(quán)限控制等功能。
工廠模式
Provider 在 export 服務(wù)時(shí),會(huì)調(diào)用 ServiceConfig 的 export 方法。ServiceConfig 中有個(gè)字段:
private static final Protocol protocol =
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Dubbo 里有很多這種代碼。這也是一種工廠模式,只是實(shí)現(xiàn)類(lèi)的獲取采用了 JDK SPI 的機(jī)制。這么實(shí)現(xiàn)的優(yōu)點(diǎn)是可擴(kuò)展性強(qiáng),想要擴(kuò)展實(shí)現(xiàn),只需要在 classpath下增加個(gè)文件就可以了,代碼零侵入。另外,像上面的 Adaptive 實(shí)現(xiàn),可以做到調(diào)用時(shí)動(dòng)態(tài)決定調(diào)用哪個(gè)實(shí)現(xiàn),但是由于這種實(shí)現(xiàn)采用了動(dòng)態(tài)代理,會(huì)造成代碼調(diào)試比較麻煩,需要分析出實(shí)際調(diào)用的實(shí)現(xiàn)類(lèi)。
裝飾器模式
Dubbo 在啟動(dòng)和調(diào)用階段都大量使用了裝飾器模式。以 Provider 提供的調(diào)用鏈為例,具體的調(diào)用鏈代碼是在 ProtocolFilterWrapper 的buildInvokerChain 完成的,具體是將注解中含有 group=provider 的 Filter 實(shí)現(xiàn),按照 order 排序,最后的調(diào)用順序是:
EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter ->
ExecuteLimitFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter ->
ExceptionFilter
更確切地說(shuō),這里是裝飾器和責(zé)任鏈模式的混合使用。例如,EchoFilter 的作用是判斷是否是回聲測(cè)試請(qǐng)求,是的話直接返回內(nèi)容,這是一種責(zé)任鏈的體現(xiàn)。而像ClassLoaderFilter 則只是在主功能上添加了功能,更改當(dāng)前線程的 ClassLoader,這是典型的裝飾器模式。
觀察者模式
Dubbo 的 Provider 啟動(dòng)時(shí),需要與注冊(cè)中心交互,先注冊(cè)自己的服務(wù),再訂閱自己的服務(wù),訂閱時(shí),采用了觀察者模式,開(kāi)啟一個(gè) listener。注冊(cè)中心會(huì)每 5 秒定時(shí)檢查是否有服務(wù)更新,如果有更新,向該服務(wù)的提供者發(fā)送一個(gè) notify 消息,provider 接受到 notify 消息后,即運(yùn)行 NotifyListener 的 notify 方法,執(zhí)行監(jiān)聽(tīng)器方法。
動(dòng)態(tài)代理模式
Dubbo 擴(kuò)展 JDK SPI 的類(lèi) ExtensionLoader 的 Adaptive 實(shí)現(xiàn)是典型的動(dòng)態(tài)代理實(shí)現(xiàn)。Dubbo 需要靈活地控制實(shí)現(xiàn)類(lèi),即在調(diào)用階段動(dòng)態(tài)地根據(jù)參數(shù)決定調(diào)用哪個(gè)實(shí)現(xiàn)類(lèi),所以采用先生成代理類(lèi)的方法,能夠做到靈活的調(diào)用。生成代理類(lèi)的代碼是 ExtensionLoader 的 createAdaptiveExtensionClassCode 方法。代理類(lèi)的主要邏輯是,獲取 URL 參數(shù)中指定參數(shù)的值作為獲取實(shí)現(xiàn)類(lèi)的 key
工作流程
整體流程:
第一步:provider 向注冊(cè)中心去注冊(cè)
第二步:consumer 從注冊(cè)中心訂閱服務(wù),注冊(cè)中心會(huì)通知 consumer 注冊(cè)好的服務(wù)
第三步:consumer 調(diào)用 provider
第四步:consumer 和 provider 都異步通知監(jiān)控中心

總結(jié)
最后用一張圖來(lái)形象的模擬 Dubbo 的使用:

以上只是我總結(jié)的一些關(guān)于 dubbo 最基礎(chǔ)的原理及使用介紹,至于代碼編寫(xiě)過(guò)程的 bug 處理經(jīng)驗(yàn),環(huán)境搭建、項(xiàng)目布局等等問(wèn)題,需要我們?cè)谄綍r(shí)開(kāi)發(fā)中,將系統(tǒng)知識(shí)與實(shí)戰(zhàn)經(jīng)驗(yàn)相結(jié)合去總結(jié),這樣才能真正的去掌握這項(xiàng)技術(shù)點(diǎn)。
Dubbo 目前是我用到過(guò)的最多的分布式框架,寫(xiě)出來(lái)的內(nèi)容也是最多的,不過(guò)由于Dubbo用的太多,而 SpringCloud 難度比 Dubbo 要小很多,現(xiàn)在大部分項(xiàng)目都即將開(kāi)始轉(zhuǎn)投到了 SpringCloud 上面,后面也會(huì)出更多的 SpringCloud 相關(guān)的文章。
關(guān)于我
下面的是我的個(gè)人二維碼圖片,希望能跟大家一起進(jìn)階,共同進(jìn)步。
推薦閱讀
再深一點(diǎn):如何給女朋友解釋什么是微服務(wù)?
別小看 Log 日志,它難住了我們組的架構(gòu)師
圖文詳解:阿里寵兒【小兔】RabbitMQ的養(yǎng)成攻略
周末給女友講了遍加密算法,沒(méi)想到…
