我們一起認識Dubbo與RPC
開個新坑,和大家一起學習Dubbo 3.X。我們按照一個由淺入深順序來學習,先從使用Dubbo開始,再深入Dubbo的核心原理。
今天我們就從認識Dubbo開始,整體的內容可以分為3個部分:
- Dubbo是什么
- RPC是什么
- Dubbo的架構
正式開始前我先疊個甲,通常網上很多資料將RPC稱之為協議,并將RPC與HTTP進行比較,目前來看這已經成為“不太正確”但主流的說法了。而我個人是個原教旨主義者,更傾向使用RPC原初的解釋,因此可能和你看到的部分文章有一定的差別。另外,因個人能力有限,若出現錯誤希望大家不吝賜教。
Tips:RPC的章節主要參考Andrew D. Birrell與Bruce Jay Nelson于1984年發表的論文《Implementing Remote Procedure Calls》,通常認為這篇文章是“現代”RPC的起源(實際上,1976年就有文獻開始討論RPC了)。
Dubbo是什么?
我們來看Apache Dubbo社區是怎樣描述Dubbo的:
Apache Dubbo是一款RPC服務開發框架,用于解決微服務架構下的服務治理與通信問題,官方提供了Java、Golang等多語言SDK實現。使用Dubbo開發的微服務原生具備相互之間的遠程地址發現與通信能力, 利用Dubbo提供的豐富服務治理特性,可以實現諸如服務發現、負載均衡、流量調度等服務治理訴求。Dubbo被設計為高度可擴展,用戶可以方便的實現流量攔截、選址的各種定制邏輯。
Dubbo是具有高性能,可拓展等特性的RPC框架,除此之外,Dubbo還提供了服務治理的能力。
Dubbo的“野心”不僅僅在于提供一套完整的RPC調用及服務治理框架,更是將Dubbo與編程語言解綁,提供了大部分主流語言的版本。
Tips:該圖截自Apache Dubbo社區在B站上發布的《5分鐘快速了解Apache Dubbo》。
RPC是什么?
既然Dubbo的本質是RPC框架,那么在繼續深入學習Dubbo前,我們有必要先來了解下RPC是什么。
RPC(Remote Procedure Call),即遠程過程調用。《Implementing Remote Procedure Calls》中是這么解釋的:
The idea of remote procedure calls (hereinafter called RPC) is quite simple. It is based on the observation that procedure calls are a well-known and well-understood mechanism for transfer of control and data within a program running on a single computer.Therefore, it is proposed that this same mechanism be extended to provide for transfer of control and data across a communication network.
RPC的思想是基于對單機程序中的傳輸和處理數據的過程調用的觀察,并建議將相同的機制拓展到遠程網絡通信上的結果。
是不是有點難理解?沒關系,我們換一個簡單點的說法,來看Sahn Lam在油管視頻《What is RPC? gRPC Introduction》中的解釋,視頻中他通過本地過程調用與遠程過程調用的對比進行解釋:
A local procedure call is a function call within a process to execute some code.A remote procedure call enables one machine to invoke some code on another machine as if it is a local fuction call from a user's perspective.
這個解釋就非常清晰了,RPC的核心是希望遠程調用可以像本地函數調用一樣簡單。Birrell與Nelson正是基于此目標,給出了RPC服務的設計參考:
Birrell與Nelson的設計是基于存根(stub,即圖中的User-stub和Server-stub)這個概念的,系統整體包含5個部分:
- 用戶端,服務調用方;
- 用戶端存根,保存函數聲明,負責請求參數的打包與響應參數的解包;
- RPC Runtime,選擇合適的方式(協議)傳輸數據;
- 服務端存根,保存函數聲明,負責請求參數的解包與響應參數的打包;
- 服務端,服務提供方。
用戶端和服務端的開發者只需要從存根中獲取并調用目標函數,而無需考慮目標函數所在服務器的地址和傳輸數據的方式,是非常契合“遠程調用可以像本地函數調用一樣簡單”這樣的愿景的。
好了,到這里我們已經對“原教旨主義”的RPC有了整體的認知,現在來回答一個不太“正經”的問題:既然有了HTTP為什么還要RPC?
這是個挺常見的初學誤區,將RPC與HTTP劃上了等號。首先RPC是一種思想(我覺得更像是簡化遠程服務調用的目標),而HTTP是應用層的傳輸協議,上圖中“兩個”RPC Runtime傳輸數據時可以使用HTTP,也可以是其它能夠完成數據傳輸的方式。其次,“現代”RPC的理論誕生于1984年,而HTTP是1989年發起的,因此這個問題反過來問還顯得稍微合理些。最后,HTTP的誕生的目的是接收和發布HTML頁面,即在瀏覽器與服務端之間進行數據的傳輸,而不是應用在兩個服務端之間的數據傳輸。
Tips:
- Sahn Lam和Alex Xu是油管頻道ByteByteGo的管理者,擁有有43萬粉絲,另外他們也是《System Design Interview》的作者;
- RPC的系統設計圖截自《Implementing Remote Procedure Calls》;
- 實際的項目中,沒有嚴格的用戶端與服務端的區分,服務都可以提供對外的接口,也可以使用外部服務的接口。
Dubbo的架構
Dubbo 3.0開始,Dubbo的官方文檔使用了新的抽象架構:
將Dubbo從整體劃分了兩層:
- Dubbo數據面:提供RPC功能的核心部分,通過RPC協議進行通信,定義了調用規范,完成了數據交互的編碼和解碼功能做;
- 服務治理控制面:服務治理的抽象,包含了注冊中心,流量管控策略,Dubbo Admin控制臺等。
Dubbo 3.0之前,官方給出了一張非常復雜的Dubbo 2.X的設計圖(以下的部分是官方原文):
圖例說明
- 圖中左邊淡藍背景的為服務消費方使用的接口,右邊淡綠色背景的為服務提供方使用的接口,位于中軸線上的為雙方都用到的接口;
- 圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關系,每一層都可以剝離上層被復用,其中,Service和Config層為API,其它各層均為SPI;
- 圖中綠色小塊的為擴展接口,藍色小塊為實現類,圖中只顯示用于關聯各層的實現類;
- 圖中藍色虛線為初始化過程,即啟動時組裝鏈,紅色實線為方法調用過程,即運行時調時鏈,紫色三角箭頭為繼承,可以把子類看作父類的同一個節點,線上的文字為調用的方法。
Dubbo提供了非常豐富的接口,這些都是Dubbo的可被用戶自定義的拓展點。Dubbo自身也采用了Microkernel+Plugin(微內核+拓展)的模式,Microkernel只負責組裝Dubbo對Plugin的默認實現。
各層說明
- config配置層:對外配置接口,以ServiceConfig,ReferenceConfig為中心,可以直接初始化配置類,也可以通過Spring解析配置生成配置類
- proxy服務代理層:服務接口透明代理,生成服務的客戶端Stub和服務器端Skeleton, 以ServiceProxy為中心,擴展接口為ProxyFactory
- registry注冊中心層:封裝服務地址的注冊與發現,以服務URL為中心,擴展接口為RegistryFactory,Registry,RegistryService
- cluster路由層:封裝多個提供者的路由及負載均衡,并橋接注冊中心,以Invoker為中心,擴展接口為Cluster,Directory,Router,LoadBalance
- monitor監控層:RPC調用次數和調用時間監控,以Statistics為中心,擴展接口為MonitorFactory,Monitor,MonitorService
- protocol遠程調用層:封裝RPC調用,以Invocation,Result為中心,擴展接口為Protocol,Invoker,Exporter
- exchange信息交換層:封裝請求響應模式,同步轉異步,以Request,Response為中心,擴展接口為Exchanger,ExchangeChannel,ExchangeClient。ExchangeServer
- transport網絡傳輸層:抽象Mina和Netty為統一接口,以Message為中心,擴展接口為Channel,Transporter,Client,Server,Codec
- serialize數據序列化層:可復用的一些工具,擴展接口為Serialization,ObjectInput,ObjectOutput,ThreadPool
有些文章會將Service納入Dubbo的層級結構中,但實際上Service是用戶業務邏輯的部分,嚴格意義上并不是Dubbo自身的組成。
支持協議
協議是RPC框架的核心功能,定義了數據的傳輸格式,除了數據本身外,還應包含控制信息,如:序列化方式,超時時間等。
Dubbo支持了非常多的協議,在這里我將它們分成5類:
不要看到Dubbo支持了這么多協議就害怕,它雖然支持的多,但我們不必每個協議都深入。未來我們在學習到協議的部分是,會重點的學習Dubbo協議,Dubbo 3.X主推的Triple協議以及支持HTTP/2的gRPC,其余協議我們大致了解其特性即可。
Tips:實際上Dubbo 2.X的官方文檔中有非常詳細的設計文檔,不知道為什么Dubbo 3.0中刪除了這部分內容。
結語
好了,到目前為止希望你能夠建立起一個對Dubbo設計的整體認知。設計雖然復雜,支持的協議雖然很多,但我們今天的目的不是“一文弄懂”。我們以理解RPC和Birrell與Nelson給出的設計為主,其次我們需要建立對Dubbo的設計的整體認知,看看它Dubbo在Birrell與Nelson的基礎上做出了哪些拓展。如果有興趣的話,可以參考Birrell與Nelson給出的架構來設計自己的RPC服務,需要考慮如何將服務保存到存根中?使用哪種方式進行交互?交互的數據結構該如何設計?