RPC系列:基本概念


RPC(Remote Procedure Call):遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的思想。

RPC 是一種技術思想而非一種規范或協議,常見 RPC 技術和框架有:

  • 應用級的服務框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。
  • 遠程通信協議:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
  • 通信框架:MINA 和 Netty。
  • ps: Google gRPC 框架是基於 HTTP2 協議實現的,底層使用到了 Netty 框架的支持。

1. RPC 框架

一個典型 RPC 的使用場景中,包含了服務發現、負載、容錯、網絡傳輸、序列化等組件,其中“RPC 協議”就指明了程序如何進行網絡傳輸和序列化。

圖 1:完整 RPC 架構圖

2. RPC 核心功能

一個 RPC 的核心功能主要有 5 個部分組成,分別是:客戶端、客戶端 Stub、網絡傳輸模塊、服務端 Stub、服務端等。

圖 4:RPC 核心功能圖

下面分別介紹核心 RPC 框架的重要組成:

  1. 客戶端(Client):服務調用方。
  2. 客戶端存根(Client Stub):存放服務端地址信息,將客戶端的請求參數數據信息打包成網絡消息,再通過網絡傳輸發送給服務端。
  3. 服務端存根(Server Stub):接收客戶端發送過來的請求消息並進行解包,然后再調用本地服務進行處理。
  4. 服務端(Server):服務的真正提供者。
  5. Network Service:底層傳輸,可以是 TCP 或 HTTP。

一次 RPC 調用流程如下:

  1. 服務消費者(Client 客戶端)通過本地調用的方式調用服務。
  2. 客戶端存根(Client Stub)接收到調用請求后負責將方法、入參等信息序列化(組裝)成能夠進行網絡傳輸的消息體。
  3. 客戶端存根(Client Stub)找到遠程的服務地址,並且將消息通過網絡發送給服務端。
  4. 服務端存根(Server Stub)收到消息后進行解碼(反序列化操作)。
  5. 服務端存根(Server Stub)根據解碼結果調用本地的服務進行相關處理
  6. 服務端(Server)本地服務業務處理。
  7. 處理結果返回給服務端存根(Server Stub)。
  8. 服務端存根(Server Stub)序列化結果。
  9. 服務端存根(Server Stub)將結果通過網絡發送至消費方。
  10. 客戶端存根(Client Stub)接收到消息,並進行解碼(反序列化)。
  11. 服務消費方得到最終結果。

RPC的目標就是要2~10這些步驟都封裝起來,讓用戶對這些細節透明。

3. RPC 核心功能實現技術點

  • 透明化遠程服務調用:字節碼生成,JDK動態代理
  • 編碼與解碼
  • 服務尋址:Call ID 映射,可以直接使用函數字符串,也可以使用整數 ID。映射表一般就是一個哈希表。
  • 數據流的序列化和反序列化:可以自己寫,也可以使用 Protobuf 或者 FlatBuffers 之類的。
  • 網絡傳輸:可以自己寫 Socket,或者用 Asio,ZeroMQ,Netty 之類。在 RPC 中可選的網絡傳輸方式有多種,可以選擇 TCP 協議、UDP 協議、HTTP 協議

3.1  透明化遠程服務調用(代理)

  1. jdk 動態代理:更多使用動態代理
  2. 字節碼生成:更為強大和高效,但代碼維護不易

3.2  服務尋址(服務注冊中心)

  實現方式:服務注冊中心。

  Call ID 映射

/**
  1) 服務尋址可以使用 Call ID 映射。在本地調用中,函數體是直接通過函數指針來指定的,但是在遠程調用中,函數指針是不行的,因為兩個進程的地址空間是完全不一樣的。

  2) 所以在 RPC 中,所有的函數都必須有自己的一個 ID。這個 ID 在所有進程中都是唯一確定的。

  3) 客戶端在做遠程過程調用時,必須附上這個 ID。然后我們還需要在客戶端和服務端分別維護一個函數和Call ID的對應表。

  4) 當客戶端需要進行遠程調用時,它就查一下這個表,找出相應的 Call ID,然后把它傳給服務端,服務端也通過查表,來確定客戶端需要調用的函數,然后執行相應函數的代碼。
**/

實現案例:RMI(Remote Method Invocation,遠程方法調用)也就是 RPC 本身的實現方式。

圖 9:RMI 架構圖

Registry(服務發現):借助 JNDI 發布並調用了 RMI 服務。實際上,JNDI 就是一個注冊表,服務端將服務對象放入到注冊表中,客戶端從注冊表中獲取服務對象。

3.3  編碼與解碼

客戶端的請求消息結構一般需要包括以下內容:
/**
    1)接口名稱:服務端就調用的那個接口;
    2)方法名:一個接口內可能有很多方法,如果不傳方法名服務端也就不知道調用哪個方法;
    3)參數類型&參數值:參數類型有很多,比如有bool、int、long、double、string、map、list,甚至如struct(class);以及相應的參數值;
    4)超時時間
    5)requestID,標識唯一請求id
*/

服務端返回的消息結構一般包括以下內容。
/**
    1)返回值
    2)狀態code
    3)requestID 
*/

3.4  序列化與反序列化

客戶端怎么把參數值傳給遠程的函數呢?在本地調用中,我們只需要把參數壓到棧里,然后讓函數自己去棧里讀就行。

但是在遠程過程調用時,客戶端跟服務端是不同的進程,不能通過內存來傳遞參數。

這時候就需要客戶端把參數先轉成一個字節流,傳給服務端后,再把字節流轉成自己能讀取的格式。

只有二進制數據才能在網絡中傳輸,序列化和反序列化的定義是:

  • 將對象轉換成二進制流的過程叫做序列化
  • 將二進制流轉換成對象的過程叫做反序列化

這個過程叫序列化和反序列化。同理,從服務端返回的值也需要序列化反序列化的過程。

目前互聯網公司廣泛使用Protobuf、Thrift、Avro等成熟的序列化解決方案來搭建RPC框架,這些都是久經考驗的解決方案。

 

3.5 網絡傳輸

網絡傳輸:遠程調用往往用在網絡上,客戶端和服務端是通過網絡連接的。

所有的數據都需要通過網絡傳輸,因此就需要有一個網絡傳輸層。網絡傳輸層需要把 Call ID 和序列化后的參數字節流傳給服務端,然后再把序列化后的調用結果傳回客戶端。

網絡協議
    1. 盡管大部分 RPC 框架都使用 TCP 協議,但其實 UDP 也可以,而 gRPC 干脆就用了 HTTP2。
    2. 在 RPC 中可選的網絡傳輸方式有多種,可以選擇 TCP 協議、UDP 協議、HTTP 協議

網絡框架
  可以自己寫 Socket,或者用 Asio,ZeroMQ,Netty 之類。

網絡通信
  目前有兩種常用IO通信模型:1)BIO;2)NIO。一般RPC框架需要支持這兩種IO模型。
  如何實現RPC的IO通信框架呢?
    1. 使用java nio方式自研,這種方式較為復雜,而且很有可能出現隱藏bug,但也見過一些互聯網公司使用這種方式;
    2. 基於mina,mina在早幾年比較火熱,不過這些年版本更新緩慢;
    3. 基於netty,現在很多RPC框架都直接基於netty這一IO通信框架,省力又省心,比如阿里巴巴的HSF、dubbo,Twitter的finagle等。

 

基於 TCP 協議的 RPC 調用

/**
    由服務的調用方與服務的提供方建立 Socket 連接,並由服務的調用方通過 Socket 將需要調用的接口名稱、方法名稱和參數序列化后傳遞給服務的提供方,服務的提供方反序列化后再利用反射調用相關的方法。
    但是在實例應用中則會進行一系列的封裝,如 RMI 便是在 TCP 協議上傳遞可序列化的 Java 對象。
*/

基於 HTTP 協議的 RPC 調用
/**
    該方法更像是訪問網頁一樣,只是它的返回結果更加單一簡單。

    其大致流程為:由服務的調用者向服務的提供者發送請求,這種請求的方式可能是 GET、POST、PUT、DELETE 等中的一種,服務的提供者可能會根據不同的請求方式做出不同的處理,或者某個方法只允許某種請求方式。

    而調用的具體方法則是根據 URL 進行方法調用,而方法所需要的參數可能是對服務調用方傳輸過去的 XML 數據或者 JSON 數據解析后的結果,返回 JOSN 或者 XML 的數據結果。

    由於目前有很多開源的 Web 服務器,如 Tomcat,所以其實現起來更加容易,就像做 Web 項目一樣。
*/

兩種方式對比
/**
    基於 TCP 的協議實現的 RPC 調用,由於 TCP 協議處於協議棧的下層,能夠更加靈活地對協議字段進行定制,減少網絡開銷,提高性能,實現更大的吞吐量和並發數。

    但是需要更多關注底層復雜的細節,實現的代價更高。同時對不同平台,如安卓,iOS 等,需要重新開發出不同的工具包來進行請求發送和相應解析,工作量大,難以快速響應和滿足用戶需求。

    基於 HTTP 協議實現的 RPC 則可以使用 JSON 和 XML 格式的請求或響應數據。

    而 JSON 和 XML 作為通用的格式標准(使用 HTTP 協議也需要序列化和反序列化,不過這不是該協議下關心的內容,成熟的 Web 程序已經做好了序列化內容),開源的解析工具已經相當成熟,在其上進行二次開發會非常便捷和簡單。

    但是由於 HTTP 協議是上層協議,發送包含同等內容的信息,使用 HTTP 協議傳輸所占用的字節數會比使用 TCP 協議傳輸所占用的字節數更高。

    因此在同等網絡下,通過 HTTP 協議傳輸相同內容,效率會比基於 TCP 協議的數據效率要低,信息傳輸所占用的時間也會更長,當然壓縮數據,能夠縮小這一差距。
*/

 

4. 摘錄網址

  1. 花了一個星期,我終於把RPC框架整明白了!
  2. RPC原理及RPC實例分析

 


免責聲明!

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



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