網(wǎng)站做3年3年包括什么b站2023年免費(fèi)入口
手動實(shí)現(xiàn)簡易版RPC(上)
前言
什么是RPC?它的原理是什么?它有什么特點(diǎn)?如果讓你實(shí)現(xiàn)一個RPC框架,你會如何是實(shí)現(xiàn)?帶著這些問題,開始今天的學(xué)習(xí)。
本文主要介紹RPC概述以及一些關(guān)于RPC的知識,為后面實(shí)現(xiàn)做充足的準(zhǔn)備。
1. RPC簡述
1.1 什么是RPC
專業(yè)定義: RPC是遠(yuǎn)程過程調(diào)用(Remote Procedure Call)。 RPC 的主要功能目標(biāo)是讓構(gòu)建分布式計(jì)算(應(yīng)用)更容易,在提供強(qiáng)大的遠(yuǎn)程調(diào)用能力時不損失本地調(diào)用的語義簡潔性。為實(shí)現(xiàn)該目標(biāo),RPC 框架需提供一種透明調(diào)用機(jī)制,讓使用者不必顯式的區(qū)分本地調(diào)用和遠(yuǎn)程調(diào)用。
舉個🌰:
假設(shè)你住在一個大社區(qū)里,社區(qū)里有很多居民,每個人都有自己的專長。比如,有的擅長修理電器,有的擅長烹飪美食,還有的擅長園藝。有一天,你突然發(fā)現(xiàn)家里的水龍頭壞了,你并不會修理,于是你想到社區(qū)里那位擅長修理電器的居民。
在這個場景下,你可以將自己想象成客戶端(Client),那位擅長修理的居民則是服務(wù)端(Server)。你想要調(diào)用服務(wù)端的“修理水龍頭”這個“方法”。但是,你和服務(wù)端并不在同一個地方,不能直接交流,于是你需要通過一些方式(比如電話,發(fā)短信,或者社區(qū)的信息平臺)來發(fā)起這個調(diào)用請求。
而達(dá)成的效果呢:就像你自己修理水龍頭一樣的絲滑,你不需要知道整個修理的過程,你只需要知道,修理好了這個結(jié)果就行
1.2 為什么要用RPC
回到 RPC 的概念,RPC 允許一個程序(稱為服務(wù)消費(fèi)者)像調(diào)用自己程序的方法一樣,調(diào)用另一個程序(稱為服務(wù)提供者)的接口,而不需要了解數(shù)據(jù)的傳輸處理過程、底層網(wǎng)絡(luò)通信的細(xì)節(jié)等。這些都會由 RPC 框架幫你完成,使得開發(fā)者可以輕松調(diào)用遠(yuǎn)程服務(wù),快速開發(fā)分布式系統(tǒng)。
舉個🌰:
現(xiàn)在有項(xiàng)目A和項(xiàng)目B兩個單獨(dú)的項(xiàng)目,項(xiàng)目A提供一系列的關(guān)于寵物的服務(wù),然后項(xiàng)目B也想使用項(xiàng)目A 的一些服務(wù),完成寵物信息的查詢。
項(xiàng)目A的查詢貓咪信息的服務(wù)接口偽代碼如下:
interface CatService {/**** 獲取貓咪信息* @return*/Cat getCat(參數(shù)1,參數(shù)2...);/**** 按照id獲取貓咪信息* @param id* @return*/Cat getCatById(int id);//....other
}
如果沒有RPC,項(xiàng)目B會如何調(diào)用項(xiàng)目A的服務(wù)呢?
首先,由于項(xiàng)目A和項(xiàng)目 B都是獨(dú)立的系統(tǒng),不能像 SDK一樣作為依賴包引入。
那么就需要項(xiàng)目 A提供 web 服務(wù),并且編寫一個點(diǎn)餐接口暴露服務(wù),比如訪問
http:localhost:8088/api/cat
就能調(diào)用服務(wù)A的貓咪查詢服務(wù);然后項(xiàng)目B作為服務(wù)消費(fèi)者,需要自己構(gòu)造請求,并通過 HttpClient 請求上述地址,拿到相關(guān)信息。如果項(xiàng)目B需要調(diào)用更多第三方服務(wù),每個服務(wù)和方法的調(diào)用都編寫一個 HTTP 請求,那么會非常麻煩
示例偽代碼如下:
url="http:localhost:8088/api/cat"
req=new Req(參數(shù)1,參數(shù)2,參數(shù)3)
res=httpClient.post(url).body(reg).execute()
cat =res.data
那么如果使用RPC框架,對于項(xiàng)目B來說,要實(shí)現(xiàn)上述調(diào)用,可能只需要一行代碼
cat=CatService.getCat(參數(shù)1,參數(shù)2,參數(shù)3)
看起來是不是和調(diào)用自己的方法一樣,十分簡潔。
2.RPC設(shè)計(jì)實(shí)現(xiàn)思路
基本設(shè)計(jì)
RPC框架為什么能夠簡化調(diào)用?該如何實(shí)現(xiàn)一個RPC框架呢?帶著這兩個問題,一起往下看
首先呢,我們將上述服務(wù)A抽象為服務(wù)提供者(producer),服務(wù)B抽象為服務(wù)消費(fèi)者(consumer)
消費(fèi)者想要調(diào)用提供者,就需要提供者啟動一個 web 服務(wù) ,然后通過 請求客戶端 發(fā)送 HTTP 或者其他協(xié)議的請求來調(diào)用。
比如請求 http:localhost:8088/api/cat
地址后,提供者會調(diào)用 CatService的 getCat方法:
但如果提供者提供了多個服務(wù)和方法,每個接口和方法是不是都要單獨(dú)寫一個接口?消費(fèi)者需不需要針對每個接口寫一段 HTTP 調(diào)用的邏輯么?
其實(shí)可以提供一個統(tǒng)一的服務(wù)調(diào)用接口,通過請求處理器
,根據(jù)客戶端的請求參數(shù)
來進(jìn)行不同的處理、調(diào)用不同的服務(wù)和方法。
可以在服務(wù)提供者程序維護(hù)一個本地服務(wù)注冊器
,記錄服務(wù)和對應(yīng)實(shí)現(xiàn)類的映射。
舉個🌰:
消費(fèi)者要調(diào)用 CatService服務(wù)的 getCat 方法,可以發(fā)送請求,參數(shù)為 service=CatService,method=getCat 然后
請求處理器
會根據(jù) service 從服務(wù)注冊器中找到對應(yīng)的服務(wù)實(shí)現(xiàn)類,并且通過 Java 的反射機(jī)制調(diào)用 method 指定的方法。
但是在數(shù)據(jù)傳輸過程中是不支持java實(shí)體類進(jìn)行傳輸?shù)?#xff0c;所以為了達(dá)成網(wǎng)絡(luò)傳輸,需要對傳輸?shù)膮?shù)等實(shí)現(xiàn)序列化和反序列化
為了簡化消費(fèi)者發(fā)請求的代碼,實(shí)現(xiàn)類似本地調(diào)用的體驗(yàn)??梢曰诖砟J?#xff0c;為消費(fèi)者要調(diào)用的接口生成一個代理對象,由代理對象完成請求和響應(yīng)的過程。所謂代理,就是有人幫你做一些事情,不用自己操心。
至此,一個最簡易的 RPC 框架架構(gòu)圖誕生了,下圖中的虛線部分:
整個簡單的調(diào)用過程客以參考下圖
拓展設(shè)計(jì)
雖然上述設(shè)計(jì)已經(jīng)跑通了基本調(diào)用流程,但離一個完備的 RPC 框架還有很大的差距,讓我們帶著問題來進(jìn)一步完善一下架構(gòu)設(shè)計(jì)。
1、服務(wù)注冊發(fā)現(xiàn)
問題 1:消費(fèi)者如何知道提供者的調(diào)用地址呢?
繼續(xù)我們上述的第一個例子,社區(qū)中有人會修理水龍頭,那么要想讓他幫你來修,你們雙方得知道雙方的地址,而這個地址,是不是由物業(yè)進(jìn)行保管。因此,我們需要一個 注冊中心,來保存服務(wù)提供者的地址。消費(fèi)者要調(diào)用服務(wù)時,只需從注冊中心獲取對應(yīng)服務(wù)的提供者地址即可。
架構(gòu)圖如下:
主流的注冊中心組件:Redis、Zookeeper、Consul、Etcd。Dubbo采用的是ZooKeeper提供服務(wù)注冊與發(fā)現(xiàn)功能。
2、負(fù)載均衡
問題 2:如果有多個服務(wù)提供者,消費(fèi)者應(yīng)該調(diào)用哪個服務(wù)提供者呢?
我們可以給服務(wù)調(diào)用方增加負(fù)載均衝能力,通過指定不同的算法來決定調(diào)用哪一個服務(wù)提供者,比如輪詢、隨機(jī)、根據(jù)性能動態(tài)調(diào)用等,在高并發(fā)的場景下,需要多個節(jié)點(diǎn)或集群來提升整體吞吐能力。。
架構(gòu)圖如下:
3、容錯機(jī)制
問題 3:如果服務(wù)調(diào)用失敗,應(yīng)該如何處理呢?
為了保證分布式系統(tǒng)的高可用,我們通常會給服務(wù)的調(diào)用增加一定的容錯機(jī)制,比如失敗重試、降級調(diào)用其他接口等等。
架構(gòu)圖如下:
4、其他
除了上面幾個經(jīng)典設(shè)計(jì)外,如果想要做一個優(yōu)秀的 RPC 框架,還要考慮很多問題。
比如:
- 服務(wù)提供者下線了怎么辦?需要一個失效節(jié)點(diǎn)剔除機(jī)制。
- 服務(wù)消費(fèi)者每次都從注冊中心拉取信息,性能會不會很差?可以使用緩存來優(yōu)化性能。
- 如何優(yōu)化 RPC 框架的傳輸通訊性能?比如選擇合適的網(wǎng)絡(luò)框架、自定義協(xié)議頭、節(jié)約傳輸體積等
- 如何讓整個框架更利于擴(kuò)展?比如使用 Java 的 SPI 機(jī)制、配置化等等。
所以,完成 RPC 項(xiàng)目并不難,但做一個完美的 RPC 項(xiàng)目卻是難于上青天啊!
總結(jié)一下,我們可以通過做一個 RPC 項(xiàng)目學(xué)習(xí)到網(wǎng)絡(luò)、序列化、代理、服務(wù)注冊發(fā)現(xiàn)、負(fù)載均衡、容錯、可擴(kuò)展設(shè)計(jì)等知識,相信完成后會收獲滿滿。