Dubbo的設計理念原來就藏在這三張圖中
阿新 • • 發佈:2021-01-22
Dubbo在眾多的微服務框架中脫穎而出,佔據RPC服務框架的半壁江山,非常具有普適性,熟練掌握 Dubbo的應用技巧後深刻理解其內部實現原理,讓大家能更好的掌控工作,助力職場,特別能讓大家在面試中脫穎而出。
那Dubbo內部的設計理念,實現原理是什麼呢?
本文將結合官方提供的3張圖,從如下三個方面介紹其內部的核心實現、以及如何指導實踐。
## 1、服務註冊與發現機制
Dubbo的服務註冊與發現機制如下圖所示:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210116210800489.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==,size_16,color_FFFFFF,t_70#pic_center)
在Dubbo中存在4類角色:
- Registry
註冊中心。
- Consumer
服務呼叫者、消費端。
- Provider
服務提供者。
- Monitor
監控中心。
具體的互動流程包括如下關鍵步驟:
1. 服務提供者在啟動的時候向註冊中心進行註冊。
2. 訊息消費者在啟動的時候向註冊中心訂閱指定服務,註冊中心將以某種機制(推或拉)模式告知消費端服務提供者列表。
3. 當服務提供者數量變化(服務提供者擴容、縮容、宕機等因素),註冊中心需要以某種方式(推或拉)告知消費端,以便消費端進行正常的負載均衡。
4. 服務提供者、服務消費者向監控中心彙報TPS等呼叫資料,以便監控中心進行視覺化展示等。
Dubbo官方提供了多種註冊中心,接下來將以使用最為普遍的Zookeeper進一步介紹註冊中心的原理。
首先我們來看一下Zookeeper註冊中心中的資料儲存目錄結構,從目錄結構來窺探其實現機制。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210116210830727.png#pic_center)
Dubbo Zookeeper註冊中心,其目錄組織結構為 /dubbo/{ServiceName},再每一個服務名稱下會有4個目錄:
- providers
服務提供者列表。
- consumers
消費者列表
- routers
路由規則列表,關於一個服務可以設定多個路由規則。
- configurators
動態配置條目。在Dubbo中可以在不重啟消費者、服務提供者的前提下動態修改服務提供者、服務消費者的配置,例如修改執行緒的數量,超時時間等引數。
基於Zookeeper註冊中心的實現細節如下:
1. 服務提供者啟動時會向註冊中心註冊,主要是在對應服務的providers目錄下增加一條記錄(**臨時節點**),同時監聽 configurators節點。
2. 服務消費者啟動時會向註冊中心訂閱,主要是在對應服務的consumers目錄下增加一條記錄(**臨時節點**),同時監聽 configurators、routers 目錄。
3. 由於當有新的服務提供者上線後 providers 目錄會增加一條記錄,消費者能立馬收到一個服務提供者列表變化的通知,得以將最新的服務提供者列表推送給服務呼叫方(消費端);如果一個服務提供者宕機,由於建立的節點是臨時節點,Zookeeper會將該節點移除,同樣會觸發事件,消費端得知最新的服務提供者列表,從而實現路由的動態註冊與發現。
4. 當Dubbo新版本上線後,如果需要進行灰度釋出,可以通過dubbo-admin等管理平臺新增路由規則,最終會寫入到指定服務的router節點(持久節點),服務呼叫方會監聽該節點的變化,從而感知最新的路由規則,將其用於服務提供者的篩選,從而實現灰度釋出等功能。
5. configurators 節點的運作機制與 router 節點一樣,就不重複介紹。
**擴充套件思考**:
1、如果註冊中心全部宕機,對整個服務體系會有什麼影響?
如果整個註冊中心全部宕機,整個**服務呼叫**能正常工作,不會影響現有的服務消費者呼叫,但消費端無法發現新註冊的服務提供者。
2、如果註冊中心記憶體溢位或頻繁發生 Full Gc,對整個叢集又會帶來什麼影響呢?
如果頻繁發生Full GC,並且如果Full GC的時間超過了Zookeeper會話的過期時間,將會造成**非常嚴重的影響**,會觸發所有臨時節點被刪除,消費端將無法感知服務提供者的存在,影響服務呼叫,將大面積丟擲 No provider 等錯誤。**正所謂成也臨時節點、敗也臨時節點**。
**為了避免Full Gc帶來的嚴重後果,用於Dubbo註冊中心的Zookeeper,一定會要獨享,並及時做好記憶體、CPU等的監控與告警。**
## 2、服務呼叫
Dubbo的服務呼叫設計十分優雅,其實現原理如下圖所示:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210116210853206.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==,size_16,color_FFFFFF,t_70#pic_center)
服務呼叫,重點闡述客戶端發起一個RPC服務呼叫時的所有實現細節,包括**服務發現**、**故障轉移**、**路由轉發**、**負載均衡**等方面,是Dubbo實現灰度釋出的理論基礎。
#### 2.1 服務發現
客戶端在向服務端發起請求時,首先需要知道的是當前有哪些可用的服務提供者,通常有兩種服務發現機制:
- 靜態化配置
不妨回想一下,在Dubbo等微服務框架出現之前,一個模組呼叫另外一個模組通常的做法是使用一個配置檔案,將服務提供的列表配置配置在配置檔案中,客戶端從按照配置檔案中的列表進行淪陷。
其**弊端**也非常明顯:如果需要呼叫的服務眾多,配置檔案會變得臃腫,對擴容縮容的管理、機器宕機等變更不友好,管理非常困難。
- 動態發現
通常基於註冊中心實現服務的註冊與動態發現,由於上文已詳細介紹,在這裡就不累述。
#### 2.2 負載均衡
客戶端通過服務發現機制,能動態發現當前存活的服務提供者列表,接下來要考慮的是如果從服務提供者列表中選擇一個服務提供者發起呼叫,這就是所謂的**負載均衡**,即 LoadBalance。
在Dubbo中預設提供了隨機、加權隨機、最少活躍連線、一致性Hash等負載均衡演算法。
#### 2.3 路由機制
其實Dubbo中不僅提供了負載均衡機制,還提供了智慧路由機制,這是實現**Dubbo灰度釋出**的理論基礎。
所謂的路由機制,是在服務提供者列表中,再設定一定的規則,進行過濾選擇,負載均衡時只從路由過濾規則篩選出來的服務提供者列表中選擇,為了更加形象的闡述路由機制的工作原理,給出如下示意圖:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210116210919335.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==,size_16,color_FFFFFF,t_70#pic_center)
上述設定了一條路由規則,即查詢機構ID為102的查詢使用者請求資訊,請傳送到新版本,即192168.3.102上,那主要在進行負載均衡之前先執行路由規則,從原始的服務提供者列表者按照路由規則進行過濾,從中挑選出符合要求的提供者列表,然後再進行負載均衡。
**路由機制的核心理念**:在進行**負載均衡之前**先對**服務提供者列表**運用**路由規則**,得出一個參與負載均衡的提供者列表。
#### 2.4 故障轉移
遠端服務呼叫通常涉及到網路等因素,客戶端向服務提供者發起RPC請求呼叫時並不一定100%成功,當呼叫失敗後該採用何種策略呢?
Dubbo提供瞭如下策略:
- failover
失敗後選擇另外一臺服務提供者進行重試,重試次數可配置,通常適合**實現冪等服務的場景**。
- failfast
快速失敗,失敗後立即返回錯誤。
- failsafe
呼叫失敗後列印錯誤日誌,返回成功,通常**用於記錄審計日誌等場景**。
- failback
呼叫失敗後,返回成功,但會在後臺定時無限次重試,重啟後不再重試。
- forking
併發呼叫,收到第一個響應結果後返回給客戶端。通常適合**實時性要求比較高的場景**,但浪費伺服器資源,通常可以通過forks引數設定併發呼叫度。
## 3、執行緒派發機制
Dubbo的通訊執行緒模型入下圖所示:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20210116210947560.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==,size_16,color_FFFFFF,t_70#pic_center)
#### 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執行緒中不能有阻塞操作,阻塞操作需要轉發到業務執行緒池**。
---
好了,本文就介紹到這裡了,**您的點贊與轉發**是對我持續輸出高質量文章最大的鼓勵。
歡迎關注公眾號『中介軟體興趣圈』,共同探究原始碼,交流高併發、架構經驗,回覆 PDF 更是可獲取大量學習資料。
![](https://oscimg.oschina.net/oscnet/up-62325a67a593e2649766657cba2d964777