《我想進大廠》之Dubbo普普通通9問
這是面試專題系列第四篇,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框架。
- E