RPC系列:基本概念

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

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

  • 應用級的服務框架:阿里的 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  服務尋址(服務註冊中心)

  實現方式:服務註冊中心。post

  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實例分析
相關文章
相關標籤/搜索