Dubbo 的設計理念原來就藏在這三張圖中
Dubbo 在衆多的微服務框架中脫穎而出,佔據 RPC 服務框架的半壁江山,非常具有普適性,熟練掌握 Dubbo 的應用技巧後深刻理解其內部實現原理,讓大家能更好的掌控工作,助力職場,特別能讓大家在面試中脫穎而出。
1、服務註冊與發現機制
-
Registry
註冊中心。 -
Consumer
服務調用者、消費端。 -
Provider
服務提供者。 -
Monitor
監控中心。
具體的交互流程包括如下關鍵步驟:
-
服務提供者在啓動的時候向註冊中心進行註冊。
-
消息消費者在啓動的時候向註冊中心訂閱指定服務,註冊中心將以某種機制(推或拉)模式告知消費端服務提供者列表。
-
當服務提供者數量變化(服務提供者擴容、縮容、宕機等因素),註冊中心需要以某種方式 (推或拉) 告知消費端,以便消費端進行正常的負載均衡。
-
服務提供者、服務消費者向監控中心彙報 TPS 等調用數據,以便監控中心進行可視化展示等。
Dubbo 官方提供了多種註冊中心,接下來將以使用最爲普遍的 Zookeeper 進一步介紹註冊中心的原理。
首先我們來看一下 Zookeeper 註冊中心中的數據存儲目錄結構,從目錄結構來窺探其實現機制。
Dubbo Zookeeper 註冊中心,其目錄組織結構爲 /dubbo/{ServiceName},在每一個服務名稱下會有 4 個目錄:
-
providers
服務提供者列表。 -
consumers
消費者列表 -
routers
路由規則列表,關於一個服務可以設置多個路由規則。 -
configurators
動態配置條目。在 Dubbo 中可以在不重啓消費者、服務提供者的前提下動態修改服務提供者、服務消費者的配置,例如修改線程的數量,超時時間等參數。
基於 Zookeeper 註冊中心的實現細節如下:
-
服務提供者啓動時會向註冊中心註冊,主要是在對應服務的 providers 目錄下增加一條記錄 (臨時節點),同時監聽 configurators 節點。
-
服務消費者啓動時會向註冊中心訂閱,主要是在對應服務的 consumers 目錄下增加一條記錄 (臨時節點),同時監聽 configurators、routers 目錄。
-
由於當有新的服務提供者上線後 providers 目錄會增加一條記錄,消費者能立馬收到一個服務提供者列表變化的通知,得以將最新的服務提供者列表推送給服務調用方 (消費端);如果一個服務提供者宕機,由於創建的節點是臨時節點,Zookeeper 會將該節點移除,同樣會觸發事件,消費端得知最新的服務提供者列表,從而實現路由的動態註冊與發現。
-
當 Dubbo 新版本上線後,如果需要進行灰度發佈,可以通過 dubbo-admin 等管理平臺添加路由規則,最終會寫入到指定服務的 router 節點 (持久節點),服務調用方會監聽該節點的變化,從而感知最新的路由規則,將其用於服務提供者的篩選,從而實現灰度發佈等功能。
-
configurators 節點的運作機制與 router 節點一樣,就不重複介紹。
擴展思考:
1、如果註冊中心全部宕機,對整個服務體系會有什麼影響?
如果整個註冊中心全部宕機,整個服務調用能正常工作,不會影響現有的服務消費者調用,但消費端無法發現新註冊的服務提供者。
2、如果註冊中心內存溢出或頻繁發生 Full Gc,對整個集羣又會帶來什麼影響呢?
如果頻繁發生 Full GC,並且如果 Full GC 的時間超過了 Zookeeper 會話的過期時間,將會造成非常嚴重的影響,會觸發所有臨時節點被刪除,消費端將無法感知服務提供者的存在,影響服務調用,將大面積拋出 No provider 等錯誤。正所謂成也臨時節點、敗也臨時節點。
爲了避免 Full Gc 帶來的嚴重後果,用於 Dubbo 註冊中心的 Zookeeper,一定會要獨享,並及時做好內存、CPU 等的監控與告警。
2、服務調用
Dubbo 的服務調用設計十分優雅,其實現原理如下圖所示:
服務調用,重點闡述客戶端發起一個 RPC 服務調用時的所有實現細節,包括服務發現、故障轉移、路由轉發、負載均衡等方面,是 Dubbo 實現灰度發佈的理論基礎。
2.1 服務發現
客戶端在向服務端發起請求時,首先需要知道的是當前有哪些可用的服務提供者,通常有兩種服務發現機制:
-
靜態化配置
不妨回想一下,在 Dubbo 等微服務框架出現之前,一個模塊調用另外一個模塊通常的做法是使用一個配置文件,將服務提供的列表配置配置在配置文件中,客戶端從按照配置文件中的列表進行輪循。其弊端也非常明顯:如果需要調用的服務衆多,配置文件會變得臃腫,對擴容縮容的管理、機器宕機等變更不友好,管理非常困難。
-
動態發現
通常基於註冊中心實現服務的註冊與動態發現,由於上文已詳細介紹,在這裏就不累述。
2.2 負載均衡
客戶端通過服務發現機制,能動態發現當前存活的服務提供者列表,接下來要考慮的是如果從服務提供者列表中選擇一個服務提供者發起調用,這就是所謂的負載均衡,即 LoadBalance。
在 Dubbo 中默認提供了隨機、加權隨機、最少活躍連接、一致性 Hash 等負載均衡算法。
2.3 路由機制
其實 Dubbo 中不僅提供了負載均衡機制,還提供了智能路由機制,這是實現 Dubbo 灰度發佈的理論基礎。
所謂的路由機制,是在服務提供者列表中,再設置一定的規則,進行過濾選擇,負載均衡時只從路由過濾規則篩選出來的服務提供者列表中選擇,爲了更加形象的闡述路由機制的工作原理,給出如下示意圖:
上述設置了一條路由規則,即查詢機構 ID 爲 102 的查詢用戶請求信息,請發送到新版本,即 192168.3.102 上,那主要在進行負載均衡之前先執行路由規則,從原始的服務提供者列表者按照路由規則進行過濾,從中挑選出符合要求的提供者列表,然後再進行負載均衡。
路由機制的核心理念:在進行負載均衡之前先對服務提供者列表運用路由規則,得出一個參與負載均衡的提供者列表。
2.4 故障轉移
遠程服務調用通常涉及到網絡等因素,客戶端向服務提供者發起 RPC 請求調用時並不一定 100% 成功,當調用失敗後該採用何種策略呢?
Dubbo 提供瞭如下策略:
-
failover
失敗後選擇另外一臺服務提供者進行重試,重試次數可配置,通常適合實現冪等服務的場景。 -
failfast
快速失敗,失敗後立即返回錯誤。
-
failsafe
調用失敗後打印錯誤日誌,返回成功,通常用於記錄審計日誌等場景。 -
failback
調用失敗後,返回成功,但會在後臺定時無限次重試,重啓後不再重試。 -
forking
併發調用,收到第一個響應結果後返回給客戶端。通常適合實時性要求比較高的場景,但浪費服務器資源,通常可以通過 forks 參數設置併發調用度。
3、線程派發機制
Dubbo 的通信線程模型入下圖所示:
3.1 網絡通信協議
網絡傳輸通常需要自定義通信協議,通常採用 Header + Body 的協議設計理念,並且 Header 長度固定,並且包含一個長度字段,用於記錄整個協議包的大小。
網絡傳輸爲了提高傳輸效率,可以採取對傳輸數據進行壓縮,通常是對 body 進行序列化與壓縮。
Dubbo 支持目前支持 java、compactedjava、nativejava、fastjson、fst、hessian2、kryo 等序列化協議。
3.2 線程派發機制
在 Dubbo 中默認會創建 200 個線程用於處理業務方法,所謂的線程派發機制就是 IO 線程如何決定何種請求轉發到哪類線程中執行。
目前 Dubbo 中所有的心跳包、網絡讀寫在 IO 線程中執行,無法通過配置進行修改。
Dubbo 提供瞭如下幾種線程派發機制 (Dispatcher):
-
all
所有的請求轉發到業務線程池中執行(除 IO 讀寫、心跳包) -
message
只有請求事件在線程池中執行,其他在 IO 線程上執行。 -
connection
請求事件在線程池中執行,連接、斷開連接事件排隊執行(含一個線程的線程池)。 -
direct
所有請求直接在 IO 線程中執行。
線程派發機制之所有會有多種策略,主要是考慮線程切換帶來的開銷是否能容忍,即線程切換帶來的開銷小於多線程處理帶來的提升。
例如在 Dubbo 中,對心跳包只需直接返回 PONG 包(OK),邏輯非常簡單,如果將其轉換到業務線程池,並不能帶來性能提升,反而因爲需要線程切換,帶來性能損耗,故在 IO 線程中直接發送響應包是一個非常可取的做法。
在網絡編程中需要遵循一條最佳實踐:IO 線程中不能有阻塞操作,阻塞操作需要轉發到業務線程池。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/LN0T3pujxIlZEEvUwMikDw