既然有 HTTP 協(xié)議,為什么還要有 RPC
HTTP和RPC
什么是HTTP
HTTP協(xié)議(Hyper Text Transfer Protocol),又叫做超文本傳輸協(xié)議。平時上網(wǎng)在瀏覽器上敲個網(wǎng)址就能訪問網(wǎng)頁,這里用到的就是HTTP協(xié)議。
什么是RPC
RPC(Remote Procedure Call),又叫做遠程過程調(diào)用。它并不是一個具體的協(xié)議,而是一種調(diào)用方式。
像之前的單體時代,我們的 service 調(diào)用就是自己實現(xiàn)的方法,是本地進程內(nèi)的調(diào)用。
public User getUserById(Long id) {
return userDao.getUserById(id); // 這叫本地調(diào)用
}
現(xiàn)在都是微服務(wù)了,根據(jù)業(yè)務(wù)模塊做了不同的拆分,像用戶的服務(wù)不用我這個小組負責,我這小組只要寫訂單服務(wù)就行了。
但是我們服務(wù)需要用到用戶的信息,于是我們需要調(diào)用用戶小組的服務(wù),于是代碼變成了以下這種
public User getUserById(Long id) {
return userConsumer.getUserById(id); // 遠程調(diào)用
}
我們像調(diào)用本地方法那樣去調(diào)用它,屏蔽掉一些網(wǎng)絡(luò)細節(jié),這樣用起來豈不是很方便。
值得注意的是,雖然大部分RPC協(xié)議底層使用TCP,但實際上它們不一定非得使用TCP,改用UDP或者HTTP,其實也可以做到類似的功能。
既然有RPC了,為什么還要有HTTP?
RPC 調(diào)用使用的場景更多的公司內(nèi)部的多個服務(wù)之間的通信。
服務(wù)的拆分獨立部署,那服務(wù)間的調(diào)用就必然需要網(wǎng)絡(luò)通信,用 Http的方式 調(diào)用當然可行,但是比較麻煩。
想要服務(wù)被拆分了但是使用起來還是和之前本地調(diào)用一樣方便,所以就出現(xiàn)了 RPC 框架,來屏蔽這些底層調(diào)用細節(jié),使得我們編碼上還是和之前本地調(diào)用相差不多。
HTTP 協(xié)議比較的冗余,RPC 都是內(nèi)部調(diào)用所以不需要太考慮通用性,只要公司內(nèi)部保持格式統(tǒng)一即可。
HTTP和RPC有什么區(qū)別
我們來看看RPC和HTTP區(qū)別比較明顯的幾個點。
服務(wù)發(fā)現(xiàn)
首先要向某個服務(wù)器發(fā)起請求,你得先建立連接,而建立連接的前提是,你得知道IP地址和端口。這個找到服務(wù)對應(yīng)的IP端口的過程,其實就是服務(wù)發(fā)現(xiàn)。
在HTTP中,你知道服務(wù)的域名,就可以通過DNS服務(wù)去解析得到它背后的IP地址。
而RPC的話,就有些區(qū)別,一般會有專門的中間服務(wù)去保存服務(wù)名和IP信息,比如consul。想要訪問某個服務(wù),就去這些中間服務(wù)去獲得IP和端口信息。
底層連接形式
以主流的HTTP1.1協(xié)議為例,其默認在建立底層TCP連接之后會一直保持這個連接(keep alive),之后的請求和響應(yīng)都會復(fù)用這條連接。
RPC,也跟HTTP類似,也是通過建立TCP長鏈接進行數(shù)據(jù)交互,但不同的地方在于,RPC協(xié)議一般還會再建個連接池,在請求量大的時候,建立多條連接放在池內(nèi),要發(fā)數(shù)據(jù)的時候就從池里取一條連接出來,用完放回去,下次再復(fù)用,可以說非常環(huán)保。
由于連接池有利于提升網(wǎng)絡(luò)請求性能,所以不少編程語言的網(wǎng)絡(luò)庫里都會給HTTP加個連接池,比如go就是這么干的。這一塊兩者也沒太大區(qū)別。
傳輸?shù)膬?nèi)容
基于TCP傳輸?shù)南ⅲ篽eader是用于標記一些特殊信息、body則是放我們真正需要傳輸?shù)膬?nèi)容。
可以看到像header里的很多信息,其實如果我們約定好之后,就不用每次都傳輸了,比如"content-type"這個字段。
RPC,因為它定制化程度更高,可以采用體積更小的protobuf或其他序列化協(xié)議去保存結(jié)構(gòu)體數(shù)據(jù),同時也不需要像HTTP那樣考慮各種瀏覽器行為,比如302重定向跳轉(zhuǎn)什么的。因此性能也會更好一些。
HTTP原理
RPC原理