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