什么是RPC
RPC(Remote Procedure Call)是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計算機(jī)程序上請求服務(wù)乱豆,而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。簡言之启昧,RPC使得程序能夠像訪問本地系統(tǒng)資源一樣玷坠,去訪問遠(yuǎn)端系統(tǒng)資源。-
RPC框架需要解決的問題
- 如何確定客戶端和服務(wù)端之間的通信協(xié)議渔工?
- 如何更高效地進(jìn)行網(wǎng)絡(luò)通信?
- 服務(wù)端提供的服務(wù)如何暴露給客戶端怠惶?
- 客戶端如何發(fā)現(xiàn)這些暴露的服務(wù)涨缚?
- 如何更高效地對請求對象和響應(yīng)結(jié)果進(jìn)行序列化和反序列化操作轧粟?
-
RPC的實現(xiàn)基礎(chǔ)
1策治、需要有非常高效的網(wǎng)絡(luò)通信,比如一般選擇Netty作為網(wǎng)絡(luò)通信框架兰吟;
2通惫、需要有比較高效的序列化框架,比如谷歌的Protobuf序列化框架混蔼;
3履腋、可靠的尋址方式(主要是提供服務(wù)的發(fā)現(xiàn)),比如可以使用Zookeeper來注冊服務(wù)等等惭嚣;
4遵湖、如果是帶會話(狀態(tài))的RPC調(diào)用,還需要有會話和狀態(tài)保持的功能晚吞;
-
RPC使用了哪些關(guān)鍵技術(shù)
-
動態(tài)代理
生成Client Stub(客戶端存根)和Server Stub(服務(wù)端存根)的時候需要用到Java動態(tài)代理技術(shù)延旧,可以使用JDK提供的原生的動態(tài)代理機(jī)制,也可以使用開源的:CGLib代理槽地,Javassist字節(jié)碼生成技術(shù)迁沫。
-
序列化和反序列化
在網(wǎng)絡(luò)中,所有的數(shù)據(jù)都將會被轉(zhuǎn)化為字節(jié)進(jìn)行傳送捌蚊,所以為了能夠使參數(shù)對象在網(wǎng)絡(luò)中進(jìn)行傳輸集畅,需要對這些參數(shù)進(jìn)行序列化和反序列化操作。
- 序列化:把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化缅糟,也就是編碼的過程挺智。
- 反序列化:把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化,也就是解碼的過程窗宦。
-
出于并發(fā)性能的考慮赦颇,傳統(tǒng)的阻塞式 IO 顯然不太合適谣辞。可以選擇Netty或者M(jìn)INA來解決NIO數(shù)據(jù)傳輸?shù)膯栴}沐扳。
-
-
Dubbo核心組件
dubbo-architecture.jpg組件 說明 Provider 服務(wù)提供者 Consumer 服務(wù)消費(fèi)者 Registry 服務(wù)注冊與發(fā)現(xiàn)的注冊中心 Monitor 統(tǒng)計服務(wù)的調(diào)用次數(shù)和調(diào)用時間的監(jiān)控中心 Container 服務(wù)運(yùn)行容器 - 服務(wù)容器負(fù)責(zé)啟動泥从,加載,運(yùn)行服務(wù)提供者沪摄。
- 服務(wù)提供者在啟動時躯嫉,向注冊中心注冊自己提供的服務(wù)。
- 服務(wù)消費(fèi)者在啟動時杨拐,向注冊中心訂閱自己所需的服務(wù)祈餐。
- 注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更哄陶,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者帆阳。
- 服務(wù)消費(fèi)者,從提供者地址列表中屋吨,基于軟負(fù)載均衡算法蜒谤,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗至扰,再選另一臺調(diào)用鳍徽。
- 服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計調(diào)用次數(shù)和調(diào)用時間敢课,定時每分鐘發(fā)送一次統(tǒng)計數(shù)據(jù)到監(jiān)控中心阶祭。
Dubbo 架構(gòu)具有以下幾個特點(diǎn),分別是連通性直秆、健壯性濒募、伸縮性、以及向未來架構(gòu)的升級性圾结。
-
為什么要Dubbo(為什么需要RPC)
為什么微服務(wù)化瑰剃?橫向擴(kuò)展、并行開發(fā)疫稿、服務(wù)治理培他、服務(wù)編排、負(fù)載均衡
-
默認(rèn)是什么通信框架遗座,還有別的選擇嗎舀凛?默認(rèn)使用的序列化框架,你還知道哪些
Dubbo 默認(rèn)使用 Netty 框架途蒋,也是推薦的選擇猛遍,另外內(nèi)容還集成有Mina、Grizzly
協(xié)議 序列化協(xié)議 場景 dubbo://
(默認(rèn))hessian 單一長連接(建立連接后,持續(xù)發(fā)送使用)和NIO異步通信懊烤,適合于數(shù)據(jù)量小但高并發(fā)的服務(wù)調(diào)用梯醒,不適合大數(shù)據(jù)量的服務(wù),如文件傳輸 rmi://
JDK 標(biāo)準(zhǔn)序列化 傳入傳出參數(shù)數(shù)據(jù)包大小混合腌紧,消費(fèi)者與提供者個數(shù)差不多茸习,可傳文件;常規(guī)遠(yuǎn)程服務(wù)方法調(diào)用,與原生RMI服務(wù)互操作 hessian://
hessian 傳入傳出參數(shù)數(shù)據(jù)包較大壁肋,提供者比消費(fèi)者個數(shù)多号胚,提供者壓力較大,可傳文件浸遗;頁面?zhèn)鬏斆ㄐ玻募鬏敚蚺c原生hessian服務(wù)互操作 http://
表單序列化 傳入傳出參數(shù)數(shù)據(jù)包大小混合跛锌,提供者比消費(fèi)者個數(shù)多弃秆,可用瀏覽器查看 webservice://
SOAP 文本序列化 系統(tǒng)集成,跨語言調(diào)用 -
服務(wù)調(diào)用是阻塞的嗎髓帽?
Dubbo 缺省協(xié)議采用單一長連接菠赚,底層實現(xiàn)是 Netty 的 NIO 異步通訊機(jī)制;基于這種機(jī)制氢卡,Dubbo 實現(xiàn)了以下幾種調(diào)用方式:
-
同步調(diào)用:一種阻塞式的調(diào)用方式锈至,即 Consumer 端代碼一直阻塞等待晨缴,直到 Provider 端返回為止译秦;
其實,Dubbo 的底層 IO 操作都是異步的击碗。Consumer 端發(fā)起調(diào)用后筑悴,得到一個 Future 對象。對于同步調(diào)用稍途,業(yè)務(wù)線程通過
Future#get(timeout)
阁吝,阻塞等待 Provider 端將結(jié)果返回;timeout
則是 Consumer 端定義的超時時間械拍。當(dāng)結(jié)果返回后突勇,會設(shè)置到此 Future,并喚醒阻塞的業(yè)務(wù)線程坷虑;當(dāng)超時時間到結(jié)果還未返回時甲馋,業(yè)務(wù)線程將會異常返回。 -
異步調(diào)用: 基于 Dubbo 底層的異步 NIO 實現(xiàn)異步調(diào)用迄损,對于 Provider 響應(yīng)時間較長的場景是必須的定躏,它能有效利用 Consumer 端的資源,相對于 Consumer 端使用多線程來說開銷較小。
<dubbo:reference id="asyncService" interface="com.alibaba.dubbo.samples.async.api.AsyncService"> <dubbo:method name="goodbye" async="true"/> </dubbo:reference>
AsyncService service = ...; String result = service.goodbye("samples");// 這里的返回值為空痊远,請不要使用 Future<String> future = RpcContext.getContext().getFuture(); ... // 業(yè)務(wù)線程可以開始做其他事情 result = future.get(); // 阻塞需要獲取異步結(jié)果時垮抗,也可以使用 get(timeout, unit) 設(shè)置超時時間
Dubbo Consumer 端發(fā)起調(diào)用后,同時通過
RpcContext.getContext().getFuture()
獲取跟返回結(jié)果關(guān)聯(lián)的Future
對象碧聪,然后就可以開始處理其他任務(wù)冒版;當(dāng)需要這次異步調(diào)用的結(jié)果時,可以在任意時刻通過future.get(timeout)
來獲取逞姿。 參數(shù)回調(diào): 參數(shù)回調(diào)有點(diǎn)類似于本地 Callback 機(jī)制壤玫,但 Callback 并不是 Dubbo 內(nèi)部的類或接口,而是由 Provider 端自定義的哼凯;Dubbo 將基于長連接生成反向代理欲间,從而實現(xiàn)從 Provider 端調(diào)用 Consumer 端的邏輯。
-
事件通知: 事件通知允許 Consumer 端在調(diào)用之前断部、調(diào)用之后或出現(xiàn)異常時猎贴,觸發(fā)
oninvoke
、onreturn
蝴光、onthrow
三個事件她渴。 可以通過在配置 Consumer 時,指定事件需要通知的方法蔑祟,如:<bean id="demoCallback" class="com.alibaba.dubbo.samples.notify.impl.NotifyImpl" /> <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.samples.notify.api.DemoService" version="1.0.0" group="cn"> <dubbo:method name="sayHello" onreturn="demoCallback.onreturn" onthrow="demoCallback.onthrow"/> </dubbo:reference>
-
-
一般使用什么注冊中心趁耗?還有別的選擇嗎?
- Multicast注冊中心: Multicast注冊中心不需要任何中心節(jié)點(diǎn)疆虚,只要廣播地址苛败,就能進(jìn)行服務(wù)注冊和發(fā)現(xiàn)。 組播受網(wǎng)絡(luò)結(jié)構(gòu)限制径簿,只適合小規(guī)模應(yīng)用或開發(fā)階段使用罢屈。組播地址段: 224.0.0.0 - 239.255.255.255
- Zookeeper注冊中心: 基于分布式協(xié)調(diào)系統(tǒng)Zookeeper實現(xiàn),采用Zookeeper的watch機(jī)制實現(xiàn)數(shù)據(jù)變更
- redis注冊中心: 基于redis實現(xiàn)篇亭,采用key/Map存儲缠捌,key存儲服務(wù)名和類型,Map中key存儲服務(wù)URL译蒂,value服務(wù)過期時間曼月。基于redis的發(fā)布/訂閱模式通知數(shù)據(jù)變更
-
Dubbo中zookeeper做注冊中心,如果注冊中心集群都掛掉,那發(fā)布者和訂閱者還能通信嗎
可以的,為什么呢?zookeeper的信息會緩存到本地作為一個緩存文件,并且轉(zhuǎn)換成
properties
對象方便使用. -
服務(wù)提供者實現(xiàn)失效踢出是什么原理
Zookeeper中節(jié)點(diǎn)是有生命周期的.具體的生命周期取決于節(jié)點(diǎn)的類型.節(jié)點(diǎn)主要分為
持久節(jié)點(diǎn)(Persistent)
和臨時節(jié)點(diǎn)(Ephemeral)
.- 臨時節(jié)點(diǎn): 臨時節(jié)點(diǎn)的生命周期和客戶端會話綁定,也就是說,如果客戶端會話失效,那么這個節(jié)點(diǎn)就會自動被清除掉
- 持久節(jié)點(diǎn):所謂持久節(jié)點(diǎn),是指在節(jié)點(diǎn)創(chuàng)建后,就一直存在,直到有刪除操作來主動清除這個節(jié)點(diǎn),也就是說不會因為創(chuàng)建該節(jié)點(diǎn)的客戶端會話失效而消失
ZK 中我們讓所有的機(jī)其都注冊一個臨時節(jié)點(diǎn),我們判斷一個機(jī)器是否可用,我們只需要判斷這個節(jié)點(diǎn)在ZK中是否存在就可以了,不需要直接去連接需要檢查的機(jī)器,降低系統(tǒng)的復(fù)雜度
-
如何解決調(diào)用鏈過長的問題
Dubbo 可以使用 Pinpoint 和 Apache Skywalking(Incubator) 實現(xiàn)分布式服務(wù)追蹤
-
核心的配置有哪些
- dubbo:consumer
- timeout 遠(yuǎn)程服務(wù)調(diào)用超時時間(毫秒)
- retries 遠(yuǎn)程服務(wù)調(diào)用重試次數(shù)柔昼,不包括第一次調(diào)用哑芹,不需要重試請設(shè)為0,僅在cluster為failback/failover時有效
- loadbalance 負(fù)載均衡策略,可選值:random,roundrobin,leastactive岳锁,分別表示:隨機(jī)绩衷,輪詢蹦魔,最少活躍調(diào)用
- async 是否缺省異步執(zhí)行,不可靠異步咳燕,只是忽略返回值勿决,不阻塞執(zhí)行線程
- dubbo:provider(Provider 上可以配置的 Consumer 端的屬性有哪些)
- protocol 協(xié)議名稱
- threads 服務(wù)線程池大小(固定大小)
- serialization dubbo協(xié)議缺省為hessian2,rmi協(xié)議缺省為java招盲,http協(xié)議缺省為json
- threadpool 線程池類型低缩,可選:fixed/cached/limit
- retries 遠(yuǎn)程服務(wù)調(diào)用重試次數(shù),不包括第一次調(diào)用曹货,不需要重試請設(shè)為0
- actives 消費(fèi)者端咆繁,最大并發(fā)調(diào)用限制
- loadbalance:負(fù)載均衡算法,默認(rèn)隨機(jī)
- timeout:方法調(diào)用超時
- dubbo:consumer
-
Dubob集群容錯有幾種方案顶籽?
- Failover Cluster:失敗自動切換玩般,當(dāng)出現(xiàn)失敗,重試其它服務(wù)器 [1]礼饱。通常用于讀操作坏为,但重試會帶來更長延遲∧餍鳎可通過 retries="2" 來設(shè)置重試次數(shù)(不含第一次)匀伏。
- Failfast Cluster:快速失敗,只發(fā)起一次調(diào)用蝴韭,失敗立即報錯够颠。通常用于非冪等性的寫操作,比如新增記錄榄鉴。
- Failsafe Cluster:失敗安全履磨,出現(xiàn)異常時,直接忽略牢硅。通常用于寫入審計日志等操作蹬耘。
- Failback Cluster:失敗自動恢復(fù),后臺記錄失敗請求减余,定時重發(fā)。通常用于消息通知操作惩系。
- Forking Cluster:并行調(diào)用多個服務(wù)器位岔,只要一個成功即返回。通常用于實時性要求較高的讀操作堡牡,但需要浪費(fèi)更多服務(wù)資源抒抬。可通過 forks="2" 來設(shè)置最大并行數(shù)晤柄。
- Broadcast Cluster:廣播調(diào)用所有提供者擦剑,逐個調(diào)用,任意一臺報錯則報錯 [2]。通常用于通知所有提供者更新緩存或日志等本地資源信息惠勒。
-
Dubbo服務(wù)降級赚抡,失敗重試怎么做?
可以通過服務(wù)降級功能臨時屏蔽某個出錯的非關(guān)鍵服務(wù)纠屋,并定義降級后的返回策略涂臣。 如電商促銷中,屏蔽“商品推薦”這些邊緣業(yè)務(wù)售担,減少一定的并發(fā)
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
-
mock=force:return+null
表示消費(fèi)方對該服務(wù)的方法調(diào)用都直接返回 null 值赁遗,不發(fā)起遠(yuǎn)程調(diào)用。用來屏蔽不重要服務(wù)不可用時對調(diào)用方的影響族铆。 - 還可以改為
mock=fail:return+null
表示消費(fèi)方對該服務(wù)的方法調(diào)用在失敗后岩四,再返回 null 值,不拋異常哥攘。用來容忍不重要服務(wù)不穩(wěn)定時對調(diào)用方的影響炫乓。
-
Dubbo使用了哪些設(shè)計模式
-
Dubbo SPI與 Java SPI區(qū)別
SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制献丑。SPI 的本質(zhì)是將接口實現(xiàn)類的全限定名配置在文件中末捣,并由服務(wù)加載器讀取配置文件,加載實現(xiàn)類创橄。這樣可以在運(yùn)行時箩做,動態(tài)為接口替換實現(xiàn)類。正因此特性妥畏,我們可以很容易的通過 SPI 機(jī)制為我們的程序提供拓展功能邦邦。
JAVA:
public interface Robot { void sayHello(); } public class OptimusPrime implements Robot { @Override public void sayHello() { System.out.println("Hello, I am Optimus Prime."); } } public class Bumblebee implements Robot { @Override public void sayHello() { System.out.println("Hello, I am Bumblebee."); } }
-
META-INF/services 文件夾下創(chuàng)建一個文件,名稱為 Robot 的全限定名 org.apache.spi.Robot
org.apache.spi.OptimusPrime org.apache.spi.Bumblebee
-
加載并使用
public class JavaSPITest { @Test public void sayHello() throws Exception { ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class); System.out.println("Java SPI"); serviceLoader.forEach(Robot::sayHello); } }
Dubbo SPI 示例
Dubbo 并未使用 Java SPI醉蚁,而是重新實現(xiàn)了一套功能更強(qiáng)的 SPI 機(jī)制燃辖。Dubbo SPI 的相關(guān)邏輯被封裝在了 ExtensionLoader 類中,通過 ExtensionLoader网棍,我們可以加載指定的實現(xiàn)類黔龟。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路徑下,配置內(nèi)容如下滥玷。
optimusPrime = org.apache.spi.OptimusPrime bumblebee = org.apache.spi.Bumblebee
與 Java SPI 實現(xiàn)類配置不同氏身,Dubbo SPI 是通過鍵值對的方式進(jìn)行配置,這樣我們可以按需加載指定的實現(xiàn)類惑畴。另外蛋欣,在測試 Dubbo SPI 時,需要在 Robot 接口上標(biāo)注 @SPI 注解如贷。下面來演示 Dubbo SPI 的用法:
public class DubboSPITest { @Test public void sayHello() throws Exception { ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); } }
-
Dubbo支持分布式事務(wù)嗎陷虎?
目前暫時不支持到踏,后續(xù)可能采用基于 JTA/XA 規(guī)范實現(xiàn)
-
Dubbo如何優(yōu)雅停機(jī)
優(yōu)雅停機(jī)是指在停止應(yīng)用時,執(zhí)行的一系列保證應(yīng)用正常關(guān)閉的操作尚猿。這些操作往往包括等待已有請求執(zhí)行完成窝稿、關(guān)閉線程、關(guān)閉連接和釋放資源等谊路,優(yōu)雅停機(jī)可以避免非正常關(guān)閉程序可能造成數(shù)據(jù)異扯锴或丟失,應(yīng)用異常等問題缠劝。優(yōu)雅停機(jī)本質(zhì)上是JVM即將關(guān)閉前執(zhí)行的一些額外的處理代碼潮梯。
Provider在接收到停機(jī)指令后
- 從注冊中心上注銷所有服務(wù);
- 從配置中心取消監(jiān)聽動態(tài)配置惨恭;
- 向所有連接的客戶端發(fā)送只讀事件秉馏,停止接收新請求;
- 等待一段時間以處理已到達(dá)的請求脱羡,然后關(guān)閉請求處理線程池萝究;
- 斷開所有客戶端連接。
Consumer在接收到停機(jī)指令后
- 拒絕新到請求锉罐,直接返回調(diào)用異常帆竹;
- 等待當(dāng)前已發(fā)送請求執(zhí)行完畢,如果響應(yīng)超時則強(qiáng)制關(guān)閉連接脓规。
-
Dubbo與Spring Cloud區(qū)別
Dubbo確實類似于Spring Cloud的一個子集栽连,Dubbo功能和文檔完善。 對于類似于電商等同步調(diào)用場景多并且能支撐搭建Dubbo 這套比較復(fù)雜環(huán)境的成本的產(chǎn)品而言侨舆,Dubbo 確實是一個可以考慮的選擇秒紧。
Spring Cloud由眾多子項目組成,如Spring Cloud Config挨下、Spring Cloud Netflix熔恢、Spring Cloud Consul 等,提供了搭建分布式系統(tǒng)及微服務(wù)常用的工具臭笆,如配置管理叙淌、服務(wù)發(fā)現(xiàn)、斷路器耗啦、智能路由凿菩、微代理、控制總線帜讲、一次性token、全局鎖椒拗、選主似将、分布式會話和集群狀態(tài)等获黔,滿足了構(gòu)建微服務(wù)所需的所有解決方案。
-
當(dāng)一個服務(wù)接口有多種實現(xiàn)時怎么做
當(dāng)一個接口有多種實現(xiàn)時在验,可以用 group 屬性來分組玷氏,服務(wù)提供方和消費(fèi)方都指定同一個 group 即可
-
Dubbo在安全機(jī)制方面是如何解決的
Dubbo通過Token令牌防止用戶繞過注冊中心直連,然后在注冊中心上管理授權(quán)腋舌。Dubbo還提供服務(wù)黑白名單盏触,來控制服務(wù)所允許的調(diào)用方。