既然有 HTTP 請求,爲什麼還要用 RPC 調用?

首先 RPC 框架配置複雜,如果走 HTTP 請求同樣也能做到相同功能,而且配置可以幾乎爲零配置。項目也不用引用太多的包。雖然如果單純用 RPC,可以不用類似於 Servlet 的 Web 標準。

這個回答裏恰巧講了一些rpc通信協議的細節,但是強調一遍通信協議不是rpc最重要的部分,不要被這個回答帶偏了。如果要了解rpc請更多的去了解服務治理(soa)的一些基本策略,推薦去看看dubbo的文檔。

這個問題其實是有理解誤區的,首先 http 和 rpc 並不是一個並行概念。

rpc是遠端過程調用,其調用協議通常包含傳輸協議和序列化協議。

傳輸協議包含: 如著名的 [gRPC](grpc / grpc.io) 使用的 http2 協議,也有如dubbo一類的自定義報文的tcp協議。

序列化協議包含: 如基於文本編碼的 xml json,也有二進制編碼的 protobuf hessian等。

因此我理解的你想問的問題應該是:爲什麼要使用自定義 tcp 協議的 rpc 做後端進程通信?

要解決這個問題就應該搞清楚 http 使用的 tcp 協議,和我們自定義的 tcp 協議在報文上的區別。

首先要否認一點 http 協議相較於自定義tcp報文協議,增加的開銷在於連接的建立與斷開。http協議是支持連接池複用的,也就是建立一定數量的連接不斷開,並不會頻繁的創建和銷燬連接。二一要說的是http也可以使用protobuf這種二進制編碼協議對內容進行編碼,因此二者最大的區別還是在傳輸協議上。

通用定義的http1.1協議的tcp報文包含太多廢信息,一個POST協議的格式大致如下

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

即使編碼協議也就是body是使用二進制編碼協議,報文元數據也就是header頭的鍵值對卻用了文本編碼,非常佔字節數。如上圖所使用的報文中有效字節數僅僅佔約 30%,也就是70%的時間用於傳輸元數據廢編碼。當然實際情況下報文內容可能會比這個長,但是報頭所佔的比例也是非常可觀的。

那麼假如我們使用自定義tcp協議的報文如下

報頭佔用的字節數也就只有16個byte,極大地精簡了傳輸內容。

這也就是爲什麼後端進程間通常會採用自定義tcp協議的rpc來進行通信的原因。

-- 分割線 2017.08.03 --

可能回答裏面沒有描述清楚,這個回答僅僅是用來糾正victory的回答的。所謂的效率優勢是針對http1.1協議來講的,http2.0協議已經優化編碼效率問題,像grpc這種rpc庫使用的就是http2.0協議。這麼來說吧http容器的性能測試單位通常是kqps,自定義tpc協議則通常是以10kqps到100kqps爲基準

簡單來說成熟的rpc庫相對http容器,更多的是封裝了“服務發現”,"負載均衡",“熔斷降級”一類面向服務的高級特性。可以這麼理解,rpc框架是面向服務的更高級的封裝。如果把一個http servlet容器上封裝一層服務發現和函數代理調用,那它就已經可以做一個rpc框架了。

所以爲什麼要用rpc調用?

因爲良好的rpc調用是面向服務的封裝,針對服務的可用性和效率等都做了優化。單純使用http調用則缺少了這些特性。

只要是遠程調用都可以叫RPC,和是不是通過http沒什麼關係。

REST就是一種常用的rpc,M紀用的實現是OData,A紀用的是CoralService,都是基於http的封裝。

另外你說http同樣功能幾乎零配置,那是胡扯。你要想達到同等級的通用性,擴展性,還加上一堆常用的feature如重試、限流、容錯等等,自個配置好http差不多拿出來也是和這幾個一個等級的框架了。你能這麼說說明你對常用rpc框架提供的功能壓根沒有什麼概念。

當然rpc也有不通過http的,可以直接走socket,或者其他協議,在不同的場景甚至有優於http的性能表現,這個很正常。用http不是因爲它性能好,而是因爲它普適,隨便一個web容器就能跑起來你的應用。

首先,實名讚揚題主的問題。這個問題非常好

其次,實名反對各個上來就講RPC好而HTTP不好的答案。因爲,題主的觀點非常對

HTTP協議,以其中的Restful規範爲代表,其優勢很大。它可讀性好,且可以得到防火牆的支持、跨語言的支持。而且,在去年的報告中,Restful大有超過RPC的趨勢

本想引用下報告內容,無奈最近由於某些原因,KeXueShangWang被Qiang了。等我日後出牆時,再做補充。

但是HTTP也有其缺點,這是與其優點相對應的。首先是有用信息佔比少,畢竟HTTP工作在第七層,包含了大量的HTTP頭等信息。其次是效率低,還是因爲第七層的緣故。還有,其可讀性似乎沒有必要,因爲我們可以引入網關增加可讀性。此外,使用HTTP協議調用遠程方法比較複雜,要封裝各種參數名和參數值。

而RPC則與HTTP互補,我們詳細介紹下。看完這篇回答,能讓你對RPC的產生、原理、實現代碼都有着清晰的瞭解。這樣,也能在業務系統中,在RPC和HTTP之間做好抉擇。

但需要再說一句,不是說RPC好,也不是說HTTP好,兩者各有千秋,還在比拼中

要問我站誰?我根據業務場景,靈活站位……

評論區產生了一些爭論,我在這裏統一進行說明。爭論主要發生在兩點:

1、HTTP和RPC同一級別,還是被RPC包含?

2、Restful也屬於RPC麼?

對於以上兩點,我畫圖來一一說明。

上圖是一個比較完整的關係圖,這時我們發現HTTP(圖中藍色框)出現了兩次。其中一個是和RPC並列的,都是跨應用調用方法的解決方案;另一個則是被RPC包含的,是RPC通信過程的可選協議之一。

因此,第一個問題的答案是都對。看指的是哪一個藍色框。從題主的提問看,既然題主在糾結這兩者,應該是指與RPC並列的藍色框。

第二個問題是在問遠程過程調用(紅色框)是不是包含了Restful(黃色框),這種理解的關鍵在於對RPC的理解。

RPC字面理解是遠程過程調用,即在一個應用中調用另一個應用的方法。那Restful是滿足的,通過它可以實現在一個應用中調用另一個應用的方法。

但是,上述理解使得RPC的定義過於寬泛。RPC通常特指在一個應用中調用另一個應用的接口而實現的遠程調用,即紅色框所指的範圍。這樣,RPC是不包含Restful的。

因此,第二個問題的答案是Restful不屬於RPC,除非對RPC有着非常規的寬泛理解。

RPC的英文全稱是Remote Procedure Call,翻譯爲中文叫“遠程過程調用”。其中稍顯晦澀的其實就是“過程”,過程其實就是方法。所以,可以把RPC理解爲“遠程方法調用”。

要了解遠程過程調用,那先理解過程調用。非常簡單,如下圖,就是調用一個方法。這太常見了,不多解釋。

而在分佈式系統中,因爲每個服務的邊界都很小,很有可能調用別的服務提供的方法。這就出現了服務A調用服務B中方法的需求,即遠程過程調用。

要想讓服務A調用服務B中的方法,最先想到的就是通過HTTP請求實現。是的,這是很常見的,例如服務B暴露Restful接口,然後讓服務A調用它的接口。基於Restful的調用方式因爲可讀性好(服務B暴露出的是Restful接口,可讀性當然好)而且HTTP請求可以通過各種防火牆,因此非常不錯。

然而,如前面所述,基於Restful的遠程過程調用有着明顯的缺點,主要是效率低、封裝調用複雜。當存在大量的服務間調用時,這些缺點變得更爲突出。

服務A調用服務B的過程是應用間的內部過程,犧牲可讀性提升效率、易用性是可取的。基於這種思路,RPC產生了。

通常,RPC要求在調用方中放置被調用的方法的接口。調用方只要調用了這些接口,就相當於調用了被調用方的實際方法,十分易用。於是,調用方可以像調用內部接口一樣調用遠程的方法,而不用封裝參數名和參數值等操作。

那要想實現這個過程該怎麼辦呢?別急,咱們一步一步來。

首先,調用方調用的是接口,必須得爲接口構造一個假的實現。顯然,要使用動態代理。這樣,調用方的調用就被動態代理接收到了。

第二,動態代理接收到調用後,應該想辦法調用遠程的實際實現。這包括下面幾步:

  • 識別具體要調用的遠程方法的IP、端口
  • 將調用方法的入參進行序列化
  • 通過通信將請求發送到遠程的方法中

這樣,遠程的服務就接收到了調用方的請求。它應該:

  • 反序列化各個調用參數
  • 定位到實際要調用的方法,然後輸入參數,執行方法
  • 按照調用的路徑返回調用的結果

整個過程如下所示。

這樣,RPC操作就完成了。

調用方調用內部的一個方法,但是被RPC框架偷樑換柱爲遠程的一個方法。之間的通信數據可讀性不需要好,只需要RPC框架能讀懂即可,因此效率可以更高。通常使用UDP或者TCP作爲通訊協議,當然也可以使用HTTP。例如下面的示例中,爲了保證實現最簡單,就用了HTTP進行通信。

講到這裏,RPC的產生原因、原理應該清楚了。爲了讓大家真的明白,我寫了一個真的是最最簡單的RPC實現。把它放到了:

https://github.com/yeecode/EasyRPC​github.com

它包含一個客戶端,一個服務端。客戶端只要調用自身內部的接口,就通過這個小的RPC實現調用到了服務端的方法。

下面是客戶端的代碼,看着類有點多,其實代碼不長。其中的RPC代碼完成完成動態代理、遠程調用參數序列化、遠程調用發起、遠程調用結果反序列化的工作。

RPC客戶端

下面是服務端的代碼,代碼更少,完成遠程調用接收、調用參數反序列化、調用實際觸發、調用結果序列化的工作。

RPC服務端

這樣,一個RPC小框架就做完了,並不複雜。

所以,不要被RPC嚇到,它就是讓一個應用調用另一個應用中方法的一種實現方式。與調用遠程接口區別不大,條條大路通羅馬。

再說一次,不是說RPC好,也不是說HTTP好,兩者各有千秋。本質上,兩者是可讀性和效率之間的抉擇通用性和易用性之間的抉擇。最終誰能發展更好,很難說。

要問我站誰?我根據業務場景,靈活站位……

如果還有什麼沒說清楚,可以留言討論。

也可以關注我,偶爾出沒解答架構設計和編程問題

有的時候我們嫌標準 HTTP 太慢/太複雜/不適合特定場景,那麼就自己發明一個新的。

http 協議還是面向桌面瀏覽器的廣域網的一個通信協議,在緩存、冪等重試乃至 Cookie 這種瀏覽器安全相關的方面做了很多功夫。網絡環境不同可能有一些不同的協議冒出來,像現在面向移動網絡,http2 在減少 RTT、優化長連接(吃我推送拉日活)之類做了很多功夫。

在數據中心網絡裏網絡環境穩定帶寬高延時低但是業務複雜,rpc 協議就主要在註冊發現、監控、負載均衡、熔斷降級方面上做功夫,重要的是標準化。rpc 協議也可以選擇建在 http/http2 之上,有現成的 nginx、curl 之類工具可以用。

而基於http請求的方式,通過請求路徑已經確定了,你要請求的服務的類,方法名,比我上面的說的方式更傻瓜化。

所以rpc爲啥不http請求,取決於你的服務端支不支持http

另外,dubbo這樣的rpc一般在公司內部系統使用,而對外的open API一般會rest或者http方式。因爲dubbo這樣的rpc會暴露服務地址,不安全,而http方式是基於域名,網絡要更安全。

大體上來說,我覺得HTTP請求是非常浪費資源的。雖然其實本質上,HTTP也是走在TCP上面的,但是HTTP請求自己增加了很多自己的信息,因此會消耗帶寬資源,以及服務器上的內存。還有一個問題,我們知道,HTTP協議其實是無連接的,因此基於這個原因,也讓通訊變得更復雜了,而且人類理解起來也困難,典型的模型不匹配,就好像OO對象和數據庫表一樣。

那麼相對來說,直接用socket就好很多,更符合代碼級別的調用這個感覺。socket上走的字節,沒有多餘的數據,也方便壓縮。而且socket的連接能夠一直保持着,這也更符合代碼級別調用的感覺。還有一點就是用socket如果在同一主機上,可以使用unix domain的通訊方式,直接使用IO,連端口占用都省了。再假如,你的架構裏面用到了docker,那麼這種通訊方式就能夠方便的溝通docker內外了,非常方便。

所以RPC框架少有用HTTP的,多是使用socket或者消息隊列。而且其實很多情況下自己就用socket或者消息隊列實現了,連RPC框架都省了。當然RPC框架還有很有意義的,比如apache的Thrift。

第一,是先有的RPC,纔有HTTP,而不是反過來。事實上HTTP 0.9也很像RPC,到1.1才穩定成今天這個樣子,也就是REST風格。

第二,用HTTP和RPC框架各有優劣,沒有絕對的好壞。對於性能需求高,開放度相對低,用RPC有它的優勢。

http就是最樸素的rpc啊

你覺得rpc框架複雜,不是因爲rpc複雜

是因爲框架複雜

說實話,現在哪個框架不復雜呢?

rpc框架包含了重試機制,路由策略,負載均衡策略,高可用策略,流量控制策略等等能用在消息處理上的功能,當然複雜了

簡單項目沒需求的話,就用ngx+http就好了呀

不能拿你家排水和城市排水系統比吧

“既然有 HTTP 請求,爲什麼還要用 RPC 調用?”

rpc是可以包含http的呀。

“首先 RPC 框架配置複雜,如果走 HTTP 請求同樣也能做到相同功能,而且配置可以幾乎爲零配置。”

rpc框架主要爲了解決一些分佈式場景下的問題,例如熔斷機制、註冊中心、負載均衡;走http請求幾乎0配置?我想題主想問的是以spring cloud基於spring boot的0配置http restful的代表與當下分佈式框架dubbo的區別吧?

這個要從背景說起了,在這些分佈式框架熱起來之前,soa被廣泛接收,但是有些大廠開始走向雲計算的道路時,延伸出“微服務”架構,這些分佈式框架就成了微服務架構中的一環。

然後市面上兩大主流分佈式框架Spring cloud和dubbo作爲兩個分支爭議很大。dubbo在先,但停更,後有噹噹dubbox擴展支持http但文檔結尾指出安全性還不完善,然後阿里的dubbo要重啓但表示安全性不會去考慮完善,因爲自己內部有一套網關。

之前soa的時代,安全性問題很多是通過esb解決的,而到了微服務的時代很多人就面臨安全性問題,大量時間裏討論前後端分離,json web token,token授權認證,oauth2,其困惑主要是安全性問題怎麼解決。

由dubbo和dubbox的設計可以看出,這些廠在使用rpc框架於內部,而且僅限java,而暴露給互聯網的肯定還是要http,這點可以從阿里他們家的淘寶開放平臺文檔裏看出來。

Spring cloud率先形成生態圈,網關解決了安全性問題。

阿里的dubbo早期就佔有大批量市場,很多大廠也在用,http網關(例如zuul)不支持dubbo的。大廠需要自己去找網關支持dubbo的那種,另外異構語言的系統也需要自行開發dubbo框架(例如在php下開發一套dubbo框架php版才能內部去遠程調用,這對那中php/.net轉java的公司非常不友好,沒能力自己搞的話,往往都要在套一層web服務得意打通),所以dubbo作爲一個rpc框架在當時的熱度有點跟風了,非大廠很難把這些架構問題解決,畢竟國內也就那麼幾家大廠。

而當時Spring cloud基於spring boot的幾乎0配置,加上全家桶,定位非常明確,就是給中小互聯網公司玩的。

最後截止2019年,dubbo進入apache,形成一整套生態圈,上面這些問題也得意解決,現在中小企業如果還誤用了當年遺留的dubbo,我建議在架構上可以作出調整,升級到Apache dubbo ,然後可以把一些異構系統也整進來,對外的接口網關也可以用起來,可以搜索Apache dubbo。

他們之間的區別是兩個方向

SOA用ESB企業服務總線把不同協議的服務和消息統一走ESB的一種服務的協議,而微服務提倡整套架構採用統一的風格。

既然是統一的風格,那麼他們之間的區別就是,一個是統一成restful,一個是統一成dubbo化。

Spring cloud採用了restful api統一微服務中的接口風格,這種風格原本就在中小企業裏廣泛使用。

dubbo則是底層socket,用在一些極致追求性能的場合,但整個生態要遷就它,例如公司內部一個部門搞java用dubbo,另一個部門搞.net,那麼搞.net部門如果業務有依賴需要調閱dubbo,那麼需要通過學習並且也用上http://dubbo.net版,否則搞java的部門需要另外開發出一套web服務並且爲它另外搞一套負載均衡。

現在很多非盈利機構或半盈利機構,如行政服務中心、醫院等,許多人也開始玩了分佈式,很顯然由於一家機構裏會有好多套系統,並且來自不同的軟件供應商,一旦核心系統上了dubbo,以後要信息打通就要招標文件上限定java語言並支持dubbo框架。但出於數據的安全,包括一些手機端/手持嵌入式設備/桌面應用程序的出現,這些也許並非java所擅長,最終還是要多搞一套,所以限定dubbo對這種機構來說也很不利的,會帶來很多額外成本。

事實上我們這座城市是有很多號稱從阿里回來的程序員,跟風微服務,忽悠給中小企業上dubbo,一個nginx能解決的問題一定要搞出一個分佈式框架來,認爲用了分佈式框架就能不用nginx和網關,最後在web應用一層還是得上nginx(笑哭)。

我們這座城市那會還流行一種面試題,“分佈式事務怎麼辦?”“斷電斷網了怎麼辦?”(笑哭)

本質是軟件系統故障與恢復的問題,因爲我們這座城市的領導和面試官很多非計算機課班出生,對這類基礎問題時常缺乏正確判斷,僥倖以爲上個分佈式框架能解決這些問題的心理。

我相信這也是很多公司上rpc框架爲了解決一些分佈式系統帶來的問題,但有些公司是盲目地上的。

聊到rpc,分佈式框架,微服務架構,也順便提一下前後端分離。

這也是軟件開發行業的一個通病吧,例如近年來流行起來vue框架,很多缺乏正確判斷的領導想把實現前後端分離的重任寄託在vue框架,也是難爲vue框架在官方介紹文檔的一開頭還要澄清一下“我不是前端框架,我只是ui框架”,言下之意就是前端範圍好大我們不敢當,我們只是前端裏某一層的框架。

這裏說一下基於 HTTP 的 RPC,比如 XMLRPC。

它幹了一件事,實際上就是把你的方法調用映射成 HTTP 請求。

如果你直接創建 HTTP API,服務端這邊沒啥費事的,但是客戶端這邊,你要寫代碼發送很多 HTTP 請求。以最流利的 HTTP 庫來看,你要用參數構造 Request 對象,用 client 對象發送它,然後從 Response 獲得響應正文,這些代碼沒辦法省(因爲 HTTP 客戶端已經足夠簡潔了),你有多少個 API 就得寫多少個。

但是如果你用 XMLRPC,你只需要把這些方法寫進一個接口裏,然後服務端客戶端各存一份。把這個接口扔給服務器,讓它找到方法的實際位置,把這個接口扔給客戶端,讓它返回一個 Mapper。然後你就能拿着這個 Mapper 調用服務器上的方法了。

總之這個東西跟 ORM(特別是 MyBatis)很像,都屬於需要一些配置但是能省很多代碼的東西。並且用的越多越省代碼。

  1. Http 的話要先走dns 再走lvs 再走nginx 鏈路太長,可用性SLA指標太低
  2. http是文本協議比其他用在rpc上的序列化 二進制協議 如 thrift protobuf等來說性能太低 ,會造成應用rt太高。舉個例子就是,在美團研發時做的分佈式ID系統leaf,用thrift的rt在1ms之內平均只有0.4ms,單臺機器QPS 5w。同時也提供了http的方式,QPS最多達到1w,rt在50ms左右
  3. 如果你的應用對SLA和rt要求都不高,怎麼好用怎麼用
  4. 有知有說http不能像調用本地方法那樣和RPC一樣,建議github搜索feign OpenFeign/feign

內部一般走RPC,因爲方便寫代碼;外部一般走http,因爲只有客戶端和瀏覽器可以touch到用戶。

http雖然通用性強,但協議本身太重,在某些調用頻繁且對帶寬限制比較嚴格的場景下,會顯得很笨拙 

實際上很多所謂的rpc框架就是在tcp上自己封裝的應用層協議。因爲是定製的協議,所以不會有http協議那麼臃腫。 

RPC協議一般有以下特點:

  1. 針對性設計封包、解包效率更高
  2. 捨棄協議層面的可讀性提高編碼效率
  3. 輕量無過載頭部信息,HTTP的頭信息規範太多
  4. RPC某些場景下可以採用UDP等其他協議,沒有TCP的握手負擔和延遲
  5. 如果使用底層二進制編碼(非文本序列化XML、JSON等)進一步提高序列化和反序列化效率

http 是短連接,rpc 是長連接

使用rpc不必要每次都創建,釋放socket鏈接

舉一個例子:

最IM應用的時候,如果使用http請求,只能是一次request 一次 reply,因爲是短連接,服務器沒法直接給客戶端發消息通知。

如果是rpc的話,由於一般是長連接,服務器就可以直接給客戶端發通知了。

雖然http2也有長連接,rpc也有短連接實現,但是一般認爲http是短連接,rpc爲長連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章