Dubbo是什么
dubbo是一款高性能的RPC(遠(yuǎn)程過程調(diào)用)框架榄融,由阿里開發(fā),后來開源捐獻(xiàn)給了Apache
Dubbo的分層圖
dubbo整體可用分為三層:business救湖,rpc愧杯,remoting
business:就是我們的業(yè)務(wù)邏輯層提供業(yè)務(wù)數(shù)據(jù)展示與更新
rpc:提供了服務(wù)暴露,服務(wù)引用鞋既,遠(yuǎn)程調(diào)用力九,集群容錯(cuò),路由過濾邑闺,負(fù)載均衡跌前,代理對(duì)象等
remoting:網(wǎng)絡(luò)傳輸協(xié)議和數(shù)據(jù)轉(zhuǎn)換封裝
Dubbo的工作原理
1.服務(wù)啟動(dòng),provider和consumer根據(jù)配置信息陡舅,連接到注冊(cè)中心register舒萎,分別向注冊(cè)中心注冊(cè)和訂閱服務(wù)
2.register根據(jù)訂閱關(guān)系,將provider信息通知給consumer,consumer收到register發(fā)送的消息將provider保存到本地臂寝,當(dāng)provider配置發(fā)生變更會(huì)通知給consumer章鲤,consumer會(huì)刷新provider信息。
3.consumer根據(jù)路由咆贬,負(fù)載均衡選擇一個(gè)provider败徊,再根據(jù)容錯(cuò)策略,經(jīng)過filter將調(diào)用次數(shù)和時(shí)間記錄再內(nèi)存掏缎,定時(shí)發(fā)送給monitor皱蹦,最后通過invoke遠(yuǎn)程調(diào)用provider。
4.provider收到consumer的遠(yuǎn)程調(diào)用信息眷蜈,進(jìn)行反序列化獲取到接口對(duì)應(yīng)的實(shí)例對(duì)象調(diào)用接口沪哺,最后進(jìn)行序列化返回給consumer。
Dubbo為什么使用invoke代理對(duì)象進(jìn)行調(diào)用
主要為了封裝接口調(diào)用過程酌儒,遠(yuǎn)程調(diào)用可以達(dá)到看上去與本地調(diào)用過程一樣辜妓。簡(jiǎn)單叁幢,明了昆稿。封裝成包裝類鹊杖。這樣可以在調(diào)用過程中進(jìn)行服務(wù)的路由冲簿,容錯(cuò)美旧,負(fù)載网严,監(jiān)控以及緩存等策略轨淌。
服務(wù)暴露過程
服務(wù)暴露基于spring容器的事件發(fā)布機(jī)制進(jìn)行服務(wù)暴露的碑隆,讀取配置信息組裝url參數(shù)鸥印,生成invoke對(duì)象勋功,包裝成exporter對(duì)象,本地緩存库说,獲取注冊(cè)中心的register實(shí)例酝润,將export注冊(cè)到注冊(cè)中心上。
服務(wù)暴露過程(源碼級(jí)別)
首先服務(wù)暴露基于spring容器的事件發(fā)布機(jī)制進(jìn)行暴露的璃弄。
1.ServiceBean實(shí)現(xiàn)了ApplicationListener接口重寫了onApplicationEvent方法要销,在事件發(fā)布會(huì)調(diào)用onApplicationEvent()方法
? ?onApplicationEvent():調(diào)用父類ServiceConfig的export()方法
? ?export():讀取配置信息,判斷是否延遲加載夏块,如果未配置直接暴露調(diào)用doexportUrls()方法
? ?doexprotUrls():獲取注冊(cè)中心地址,多協(xié)議進(jìn)行多暴露調(diào)用doExportUrlsFor1Protocol()方法
? ?doExportUrlsFor1Protocol():組裝參數(shù)存儲(chǔ)到map中疏咐,根據(jù)map的參數(shù)及協(xié)議構(gòu)造出url,獲取url中的scope參數(shù)脐供,此參數(shù)是判斷本地暴露還是遠(yuǎn)程暴露浑塞,如果scope="local"為本地暴露,如果scope="remote"遠(yuǎn)程暴露政己,如果scope=""為空酌壕,本地和遠(yuǎn)程都進(jìn)行暴露。
? ?本地暴露調(diào)用exportLocal()方法:將url的協(xié)議更新為"injvm",通過ProxyFacotry創(chuàng)建接口的invoke實(shí)例也就是接口實(shí)現(xiàn)類,調(diào)用InJvmProtocol.export()包裝成export對(duì)象,將export本地緩存到invokes中卵牍。
? ?遠(yuǎn)程暴露:將協(xié)議和export封裝到url中果港,如:url=dubbo://xxx?export=xxx這種形式,通過ProxyFactory獲取invoke對(duì)象糊昙,調(diào)用RegisterProtocol.export()方法辛掠,接下來獲取注冊(cè)中心url和服務(wù)提供者的url,調(diào)用doLocalExport()遠(yuǎn)程暴露释牺,封裝Invoke調(diào)用DubboProtocol.export()方法萝衩,將invoke封裝成DubboExport,緊接著進(jìn)行判斷server服務(wù)是否存在没咙,是否開啟等猩谊,如果第一次暴露會(huì)創(chuàng)建server服務(wù),綁定端口祭刚,開啟服務(wù)操作等牌捷,這些操作完成之后將exporter地址注冊(cè)到注冊(cè)中心上,將export本地緩存invokers中袁梗,至此服務(wù)暴露到此結(jié)束宜鸯。
服務(wù)引用過程
服務(wù)引用有兩個(gè)策略(懶漢式與餓漢式)
餓漢式:基于spring bean的生命周期回調(diào)機(jī)制憔古,在實(shí)現(xiàn)InitializingBean的afterPropertieSet()方法進(jìn)行引入
懶漢式:別的bean類在初始化時(shí)引入的rpc時(shí)遮怜,調(diào)用beanFactory獲取ref會(huì)進(jìn)行服務(wù)的引入。
通過getObject()方法進(jìn)行服務(wù)引用鸿市,組裝參數(shù)存儲(chǔ)到map锯梁,本地引用創(chuàng)建invoke代理類即可,遠(yuǎn)程引用焰情,將consumer注冊(cè)到注冊(cè)中心上陌凳,訂閱該接口的providers,configurators内舟,routes目錄合敦,通過通知信息,將routers验游,conifgurators充岛,providers進(jìn)行更新,providers信息就是該接口的所有export地址耕蝉,將providers的各個(gè)export地址轉(zhuǎn)換成invoke對(duì)象崔梗,這個(gè)過程會(huì)通過ip端口創(chuàng)建連接,將client實(shí)例封裝在invoke中垒在,cluster路由隨機(jī)選出一個(gè)invoke實(shí)例蒜魄,創(chuàng)建該invoke的代理對(duì)象,服務(wù)引用完成。
服務(wù)引用過程(源碼級(jí)別)
兩種方式引用都是通過ReferenceBean的getObject()方法中的get()方法谈为,會(huì)調(diào)用父類的ReferenceConfig類的get()方法旅挤,讀取配置,判斷是ref引用是否為空峦阁,如果null進(jìn)行初始化調(diào)用init()方法谦铃。
? ?init():組裝參數(shù)存儲(chǔ)到map中,調(diào)用createProxy()方法生成代理對(duì)象榔昔。
? ?createProxy():判斷是否時(shí)本地引用驹闰,是否時(shí)peer-to-peer引用,注冊(cè)中心引用撒会。
本地引用:調(diào)用InJvmProtocol.refer()方法嘹朗,通過父類AbstractProtocol.refer(),在通過InJvmProtocol.refer(),創(chuàng)建invoke對(duì)象诵肛,生成invoke代理對(duì)象屹培,進(jìn)行引用。
直連引用與遠(yuǎn)程引用怔檩,組裝url參數(shù)褪秀,進(jìn)行創(chuàng)建引用。調(diào)用RegisterProtocol.refer()方法薛训,創(chuàng)建url信息媒吗,通過url獲取注冊(cè)中心實(shí)例,調(diào)用doRefer()方法乙埃,創(chuàng)建RegisterDerectory對(duì)象闸英,設(shè)置注冊(cè)中心實(shí)例,協(xié)議等介袜,將consumer注冊(cè)到注冊(cè)中心上甫何,訂閱該接口的providers,routers遇伞,configurators的目錄辙喂,當(dāng)?shù)谝淮螁?dòng)或者該目錄信息更新都會(huì)通知到RegisterDerectory的notify接口(RegisterDerectory實(shí)現(xiàn)了NotifyListener接口)。目錄信息通知notify接口更新或重寫本地invoke信息鸠珠,通過providers的urls創(chuàng)建invoke巍耗,根據(jù)協(xié)議自適應(yīng)擴(kuò)展調(diào)用DubboProtocol.refer()方法,創(chuàng)建DubboInvoker跳芳,在創(chuàng)建DubboInvoker會(huì)先獲取服務(wù)提供者的client(ExchangeClient)實(shí)例芍锦,通過getClients()方法獲取,如果是共享客戶端飞盆,獲取即可娄琉,如果客戶端為創(chuàng)建次乓,進(jìn)行初始化客戶端,根據(jù)provider的ip孽水,端口信息票腰,與provider進(jìn)行建立連接。創(chuàng)建DubboInvoer對(duì)象返回女气,redirect會(huì)持有所有的invoker對(duì)象杏慰,只需要引用某個(gè)一個(gè)即可,創(chuàng)建該invoke代理對(duì)象即可炼鞠,至此服務(wù)引用完成缘滥。
服務(wù)調(diào)用過程
使用rpc代理對(duì)象調(diào)用某個(gè)接口方法,通過Cluster過濾匹配符合匹配規(guī)則的invoke谒主,在通過負(fù)載均衡出一個(gè)invoke朝扼,通過client進(jìn)行遠(yuǎn)程調(diào)用,這個(gè)過程會(huì)有容錯(cuò)機(jī)制霎肯,監(jiān)控信息的filter上報(bào)擎颖,創(chuàng)建DefaultFuture 每個(gè)請(qǐng)求生成唯一的id存儲(chǔ)到channel和future中,遠(yuǎn)程調(diào)用后將future存儲(chǔ)到concurrenthashmap中观游,通過請(qǐng)求的id獲取future進(jìn)行g(shù)et()獲取返回的信息搂捧,反序列化等操作返回給調(diào)用者。
Dubbo的路由策略有哪些
服務(wù)調(diào)用過程中AbstractClusterInvoker抽象類中調(diào)用list()方法獲取過濾后的invokers懂缕,在過濾過程中有可選的路由策略分別是:ConditionRouter(條件路由)允跑,TagRouter(標(biāo)簽路由),ScriptRouter(腳本路由)
Dubbo的負(fù)載均衡算法有哪些
服務(wù)調(diào)用過程中AbstractClusterInvoker調(diào)用loadBalance()方法獲取配置的負(fù)載均衡算法提佣,url配置的loadbalance參數(shù)吮蛹。負(fù)載均衡算法有:
RandomLoadBalance:權(quán)重隨機(jī)算法
RandomRobinLoadBalance:輪詢算法
LastActiveLoadBalance:服務(wù)活躍度算法
ConsistentHashLoadBalance:一致性哈希算法
當(dāng)然也可以自定義一些算法荤崇,只需要在META-INFO/dubbo/目錄下創(chuàng)建文件基于dubbo的spi機(jī)制
Dubbo的容錯(cuò)機(jī)制策略有哪些
服務(wù)調(diào)用過程中拌屏,通過路由,獲取負(fù)載均衡算法后术荤,使用容錯(cuò)策略進(jìn)行遠(yuǎn)程調(diào)用,
容錯(cuò)策略包含:
FailoverCluster:重試機(jī)制倚喂。調(diào)用失敗后,重新選擇一個(gè)client進(jìn)行重試
FailbackCluster:失敗自動(dòng)恢復(fù)瓣戚,調(diào)用失敗端圈,記錄在內(nèi)存的失敗列表中,會(huì)有單獨(dú)的線程進(jìn)行異步重試調(diào)用一次子库,調(diào)用方無感知
FailfastCluster:快速失敗舱权。調(diào)用失敗,立刻拋出異常
FailsafeCluster:安全失敗仑嗅,調(diào)用失敗宴倍,捕獲異常张症,返回空結(jié)果,忽略此次調(diào)用鸵贬,將調(diào)用鏈記錄在日志上俗他,
ForkingCluster:并行策略,用向所有的服務(wù)提供方發(fā)出請(qǐng)求阔逼,有一個(gè)返回有結(jié)果就立刻返回?cái)?shù)據(jù)
BroadcastCluster:廣播策略兆衅,調(diào)用向所有的服務(wù)提供方發(fā)出請(qǐng)求,有一個(gè)發(fā)生異常此次調(diào)用認(rèn)為失敗的嗜浮。
序列化有哪些
Hessian羡亩,dubbo,fastJson危融,java序列化
支持的協(xié)議
dubbo協(xié)議:傳輸數(shù)據(jù)量小夕春,在一百k以內(nèi),但要求并發(fā)量很高专挪。consumer與provider維持一個(gè)長(zhǎng)連接及志,持續(xù)發(fā)送請(qǐng)求,配合著NIO異步通信寨腔,可以支持高并發(fā)量的請(qǐng)求
rmi協(xié)議:使用java序列化速侈,多個(gè)短連接,使用與消費(fèi)者與生產(chǎn)者差不多情況迫卢,一般使用文件傳輸
hessian協(xié)議:多個(gè)短連接倚搬,適用于服務(wù)提供者多于服務(wù)消費(fèi)者。
redis協(xié)議:基于redis實(shí)現(xiàn)rpc協(xié)議
http協(xié)議:基于http表單提交的遠(yuǎn)程調(diào)用協(xié)議乾蛤,短連接每界,傳入?yún)?shù)大小混合,生產(chǎn)者多于消費(fèi)者家卖。
Dubbo的SPI
要知道dubbo的spi就需要了解java的spi機(jī)制眨层,java只是使用自定義的擴(kuò)展實(shí)現(xiàn)類需要在resource目錄下創(chuàng)建META-INF/services目錄,創(chuàng)建一個(gè)要實(shí)現(xiàn)接口的全限定名作為文件名稱上荡,內(nèi)容是實(shí)現(xiàn)該接口的類全限定名稱趴樱。要使用某個(gè)實(shí)現(xiàn)類,需要將該文件下的所有類都加載酪捡,循環(huán)遍歷實(shí)現(xiàn)類動(dòng)態(tài)判斷要使用的哪個(gè)實(shí)現(xiàn)類叁征。由此可以得出結(jié)論,需要將配置的實(shí)現(xiàn)類都要進(jìn)行實(shí)例化逛薇,如果初始化某個(gè)實(shí)現(xiàn)類比較耗資源捺疼,但是實(shí)際項(xiàng)目中又用不上它,就會(huì)產(chǎn)生資源的浪費(fèi)永罚。正因?yàn)榇嗽駾ubbo在此基礎(chǔ)上進(jìn)行了擴(kuò)展啤呼,按需加載议薪,通過key=xxx具體的實(shí)現(xiàn)類,kv形式媳友,通過key找對(duì)應(yīng)的實(shí)現(xiàn)類斯议,約定的目錄有是哪個(gè)META-INF/services/,META-INF/dubbo(用戶自定義)醇锚,META-INF/dubbo/internal/(dubbo內(nèi)部使用的)哼御,dubbo的spi還實(shí)現(xiàn)類Adaptive注解,在使用該注解的接口方法上會(huì)生成一個(gè)字節(jié)碼的代理類通過名稱動(dòng)態(tài)的獲取接口實(shí)現(xiàn)類焊唬。