這是面試專題系列第四篇,Dubbo系列。Dubbo本身並不復雜,而且官方文檔寫的非常清楚詳細,面試中dubbo的問題一般不會很多,從分層到工作原理、負載均衡策略、容錯機制、SPI機制基本就差不多了,最大的一道大題一般就是怎么設計一個RPC框架了,但是如果你工作原理分層都搞明白了這個問題其實也就相當於回答了不是嗎。
說說Dubbo的分層?
從大的范圍來說,dubbo分為三層,business業務邏輯層由我們自己來提供接口和實現還有一些配置信息,RPC層就是真正的RPC調用的核心層,封裝整個RPC的調用過程、負載均衡、集群容錯、代理,remoting則是對網絡傳輸協議和數據轉換的封裝。
划分到更細的層面,就是圖中的10層模式,整個分層依賴由上至下,除開business業務邏輯之外,其他的幾層都是SPI機制。
能說下Dubbo的工作原理嗎?
-
服務啟動的時候,provider和consumer根據配置信息,連接到注冊中心register,分別向注冊中心注冊和訂閱服務
-
register根據服務訂閱關系,返回provider信息到consumer,同時consumer會把provider信息緩存到本地。如果信息有變更,consumer會收到來自register的推送
-
consumer生成代理對象,同時根據負載均衡策略,選擇一台provider,同時定時向monitor記錄接口的調用次數和時間信息
-
拿到代理對象之后,consumer通過代理對象發起接口調用
-
provider收到請求后對數據進行反序列化,然后通過代理調用具體的接口實現
為什么要通過代理對象通信?
主要是為了實現接口的透明代理,封裝調用細節,讓用戶可以像調用本地方法一樣調用遠程方法,同時還可以通過代理實現一些其他的策略,比如:
1、調用的負載均衡策略
2、調用失敗、超時、降級和容錯機制
3、做一些過濾操作,比如加入緩存、mock數據
4、接口調用數據統計
說說服務暴露的流程?
- 在容器啟動的時候,通過ServiceConfig解析標簽,創建dubbo標簽解析器來解析dubbo的標簽,容器創建完成之后,觸發ContextRefreshEvent事件回調開始暴露服務
- 通過ProxyFactory獲取到invoker,invoker包含了需要執行的方法的對象信息和具體的URL地址
- 再通過DubboProtocol的實現把包裝后的invoker轉換成exporter,然后啟動服務器server,監聽端口
- 最后RegistryProtocol保存URL地址和invoker的映射關系,同時注冊到服務中心
說說服務引用的流程?
服務暴露之后,客戶端就要引用服務,然后才是調用的過程。
-
首先客戶端根據配置文件信息從注冊中心訂閱服務
-
之后DubboProtocol根據訂閱的得到provider地址和接口信息連接到服務端server,開啟客戶端client,然后創建invoker
-
invoker創建完成之后,通過invoker為服務接口生成代理對象,這個代理對象用於遠程調用provider,服務的引用就完成了
有哪些負載均衡策略?
-
加權隨機:假設我們有一組服務器 servers = [A, B, C],他們對應的權重為 weights = [5, 3, 2],權重總和為10。現在把這些權重值平鋪在一維坐標值上,[0, 5) 區間屬於服務器 A,[5, 8) 區間屬於服務器 B,[8, 10) 區間屬於服務器 C。接下來通過隨機數生成器生成一個范圍在 [0, 10) 之間的隨機數,然后計算這個隨機數會落到哪個區間上就可以了。
-
最小活躍數:每個服務提供者對應一個活躍數 active,初始情況下,所有服務提供者活躍數均為0。每收到一個請求,活躍數加1,完成請求后則將活躍數減1。在服務運行一段時間后,性能好的服務提供者處理請求的速度更快,因此活躍數下降的也越快,此時這樣的服務提供者能夠優先獲取到新的服務請求。
-
一致性hash:通過hash算法,把provider的invoke和隨機節點生成hash,並將這個 hash 投射到 [0, 2^32 - 1] 的圓環上,查詢的時候根據key進行md5然后進行hash,得到第一個節點的值大於等於當前hash的invoker。
圖片來自dubbo官方
- 加權輪詢:比如服務器 A、B、C 權重比為 5:2:1,那么在8次請求中,服務器 A 將收到其中的5次請求,服務器 B 會收到其中的2次請求,服務器 C 則收到其中的1次請求。
集群容錯方式有哪些?
-
Failover Cluster失敗自動切換:dubbo的默認容錯方案,當調用失敗時自動切換到其他可用的節點,具體的重試次數和間隔時間可用通過引用服務的時候配置,默認重試次數為1也就是只調用一次。
-
Failback Cluster快速失敗:在調用失敗,記錄日志和調用信息,然后返回空結果給consumer,並且通過定時任務每隔5秒對失敗的調用進行重試
-
Failfast Cluster失敗自動恢復:只會調用一次,失敗后立刻拋出異常
-
Failsafe Cluster失敗安全:調用出現異常,記錄日志不拋出,返回空結果
-
Forking Cluster並行調用多個服務提供者:通過線程池創建多個線程,並發調用多個provider,結果保存到阻塞隊列,只要有一個provider成功返回了結果,就會立刻返回結果
-
Broadcast Cluster廣播模式:逐個調用每個provider,如果其中一台報錯,在循環調用結束后,拋出異常。
了解Dubbo SPI機制嗎?
SPI 全稱為 Service Provider Interface,是一種服務發現機制,本質是將接口實現類的全限定名配置在文件中,並由服務加載器讀取配置文件,加載實現類,這樣可以在運行時,動態為接口替換實現類。
Dubbo也正是通過SPI機制實現了眾多的擴展功能,而且dubbo沒有使用java原生的SPI機制,而是對齊進行了增強和改進。
SPI在dubbo應用很多,包括協議擴展、集群擴展、路由擴展、序列化擴展等等。
使用方式可以在META-INF/dubbo目錄下配置:
key=com.xxx.value
然后通過dubbo的ExtensionLoader按照指定的key加載對應的實現類,這樣做的好處就是可以按需加載,性能上得到優化。
如果讓你實現一個RPC框架怎么設計?
- 首先需要一個服務注冊中心,這樣consumer和provider才能去注冊和訂閱服務
- 需要負載均衡的機制來決定consumer如何調用客戶端,這其中還當然要包含容錯和重試的機制
- 需要通信協議和工具框架,比如通過http或者rmi的協議通信,然后再根據協議選擇使用什么框架和工具來進行通信,當然,數據的傳輸序列化要考慮
- 除了基本的要素之外,像一些監控、配置管理頁面、日志是額外的優化考慮因素。
那么,本質上,只要熟悉一兩個RPC框架,就很容易想明白我們自己要怎么實現一個RPC框架。
- END -