RPC 和 HTTP 理解,看完這一篇就夠了


  網上充斥着各類類似於這樣的文章:rpc 比 http 快了多少倍?既然有了 http,為什么還要用 rpc 調用等等。遇到這類文章,說明對 http 和 rpc 是由理解誤區的。

  這里再次重復強調一遍,通信協議不是 rpc 最重要的部分,不要被這類回答帶偏。如果要了解 rpc 請更多的去了解服務治理(SOA)的一些基本策略,推薦去看看 dubbo 的相關文檔。

詳解

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

  傳輸協議

  比如著名的 grpc,它底層使用的是 http2 協議;還有 dubbo 一類的自定義報文的 tcp 協議

  序列化協議

  例如基於文本編碼的 json 協議;也有二進制編碼的 protobuf、hession 等協議;還有針對 java 高性能、高吞吐量的 kryo 和 ftc 等序列化協議

  因此我理解大部人理解誤區的問題應該是:

  為什么要使用自定義 tcp 協議的 rpc 做后端進程通信?

解答

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

  首先要 否認 一點 http 協議相較於 自定義tcp 報文協議,增加的開銷在於連接的建立與斷開

  第一、http協議是支持連接池復用的,也就是建立一定數量的連接不斷開,並不會頻繁的創建和銷毀連接

  第二、http也可以使用 protobuf 這種二進制編碼協議對內容進行編碼

  因此二者即 http 和 rpc 最大的區別還是在傳輸協議上

  通用定義的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 來進行通信的原因。

不單效率那么簡單

  所謂的效率優勢是針對 http1.1協議 來講的,http2.0協議 已經優化編碼效率問題,像 grpc 這種 rpc 庫使用的就是 http2.0協議。這么來說吧,http容器的性能測試單位通常是kqps,自定義tpc協議則通常是以 10kqps 到 100kqps 為基准

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

所以為什么要用rpc調用?

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

  可以這樣說:用http不是因為它性能好,而是因為它普適,隨便一個web容器就能跑起來你的應用。

RPC 底層是怎么實現的

  之前有看過幾個帖子,評論區有激烈的爭吵,主要圍繞兩點,具體如下:

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

  2. Restful 也屬於 RPC 嗎?

  對於以上兩個問題,這里用一個圖來一一說明:

  

  上圖是一個比較完整的關系圖,這時我們發現HTTP(圖中藍色框)出現了兩次。

  其中一個是 和 RPC並列的,都是跨應用調用方法的解決方案;

  另一個則是被RPC包含的,是RPC通信過程的可選協議之一。

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

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

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

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

  因此,第二個問題的答案是Restful不屬於RPC。

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

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

  

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

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

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

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

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

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

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

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

    1. 識別具體要調用的遠程方法的 IP、端口

    2. 將調用方法的入參進行序列化

    3. 通過通信將請求發送到遠程的方法中

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

    1. 反序列化各個調用參數

    2. 定位到實際要調用的方法,然后輸入參數,執行方法

    3. 按照調用的路徑返回調用的結果

  整個過程如下所示。

  

   這樣,RPC操作就完成了。

  調用方調用內部的一個方法,但是被RPC框架偷梁換柱為遠程的一個方法。之間的通信數據可讀性不需要好,只需要RPC框架能讀懂即可,因此效率可以更高。通常使用UDP或者TCP作為通訊協議,當然也可以使用HTTP。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM