RPC

本地過程調用json

RPC就是要像調用本地的函數同樣去調遠程函數。在研究RPC前,咱們先看看本地調用是怎麼調的。假設咱們要調用函數Multiply來計算lvalue * rvalue的結果:服務器

1 int Multiply(int l, int r) {
2     int y = l * r;
3     return y;
4 }
5  
6 int lvalue = 10;
7 int rvalue = 20;
8 int l_times_r = Multiply(lvalue, rvalue);

那麼在第8行時,咱們實際上執行了如下操做:網絡

  1. 將 lvalue 和 rvalue 的值壓棧
  2. 進入Multiply函數,取出棧中的值10 和 20,將其賦予 l 和 r
  3. 執行第2行代碼,計算 l * r ,並將結果存在 y
  4. 將 y 的值壓棧,而後從Multiply返回
  5. 第8行,從棧中取出返回值 200 ,並賦值給 l_times_r

以上5步就是執行本地調用的過程。框架

 

遠程過程調用帶來的新問題socket

在遠程調用時,咱們須要執行的函數體是在遠程的機器上的,也就是說,Multiply是在另外一個進程中執行的。這就帶來了幾個新問題:函數

  1. Call ID映射。咱們怎麼告訴遠程機器咱們要調用Multiply,而不是Add或者FooBar呢?在本地調用中,函數體是直接經過函數指針來指定的,咱們調用Multiply,編譯器就自動幫咱們調用它相應的函數指針。可是在遠程調用中,函數指針是不行的,由於兩個進程的地址空間是徹底不同的。因此,在RPC中,全部的函數都必須有本身的一個ID。這個ID在全部進程中都是惟一肯定的。客戶端在作遠程過程調用時,必須附上這個ID。而後咱們還須要在客戶端和服務端分別維護一個 {函數 <--> Call ID} 的對應表。二者的表不必定須要徹底相同,但相同的函數對應的Call ID必須相同。當客戶端須要進行遠程調用時,它就查一下這個表,找出相應的Call ID,而後把它傳給服務端,服務端也經過查表,來肯定客戶端須要調用的函數,而後執行相應函數的代碼。
  2. 序列化和反序列化。客戶端怎麼把參數值傳給遠程的函數呢?在本地調用中,咱們只須要把參數壓到棧裏,而後讓函數本身去棧裏讀就行。可是在遠程過程調用時,客戶端跟服務端是不一樣的進程,不能經過內存來傳遞參數。甚至有時候客戶端和服務端使用的都不是同一種語言(好比服務端用C++,客戶端用Java或者Python)。這時候就須要客戶端把參數先轉成一個字節流,傳給服務端後,再把字節流轉成本身能讀取的格式。這個過程叫序列化和反序列化。同理,從服務端返回的值也須要序列化反序列化的過程。
  3. 網絡傳輸。遠程調用每每用在網絡上,客戶端和服務端是經過網絡鏈接的。全部的數據都須要經過網絡傳輸,所以就須要有一個網絡傳輸層。網絡傳輸層須要把Call ID和序列化後的參數字節流傳給服務端,而後再把序列化後的調用結果傳回客戶端。只要能完成這二者的,均可以做爲傳輸層使用。所以,它所使用的協議實際上是不限的,能完成傳輸就行。儘管大部分RPC框架都使用TCP協議,但其實UDP也能夠,而gRPC乾脆就用了HTTP2。Java的Netty也屬於這層的東西。

因此,要實現一個RPC框架,其實只須要把以上三點實現了就基本完成了。優化

Call ID映射能夠直接使用函數字符串,也可使用整數ID。映射表通常就是一個哈希表。編碼

序列化反序列化能夠本身寫,也可使用Protobuf或者FlatBuffers之類的。spa

網絡傳輸庫能夠本身寫socket,或者用asio,ZeroMQ,Netty之類。指針

 

========================================================================================================================================

 

根據字面意思來推斷,RPC 的確是爲了進程間通訊而準備的,但構形成函數調用這一形式,是由於這是在抽象上最合理的。

咱們能夠推斷演進一下
====
1. A B 兩個進程之間須要進行數據交換。
2.因而咱們想出來在某個內存區域劃出一個空間,而後向該空間中寫入和讀取數據。(共享文件也能夠)(常見的socket就是這一共享內存的抽象,只是如今大多指網絡通路)
3.A B 通訊完成。
====
4.A B須要完成更復雜的交互
5.因而咱們指定一個協議,A B 根據該協議對數據的進行編碼解碼,根據協議內容作出決策。
====
6.發現協議過於複雜(好比 編號1表明調用 a函數,編號2表明b函數)
7.試圖優化協議,將函數參數和調用的函數名稱做爲協議的一部分,函數返回值相似
8.RPC達成
=====
9.表現出來的特性就是,object invok(parameter),就表明了,序列化 parameter 對象到中間格式,利用遠程服務器的 invok 函數進行處理 ,同時將返回的數據解碼生成 object對象。

======總結=====

RPC 在整個過程當中,體現了逐層抽象,將複雜的協議編解碼和數據傳輸封裝到了一個函數中。

======缺點=====
單一 RPC 沒法實現 push,即推送服務。
理由是,RPC 是client 調用 server獲取數據,是一個完整的過程,實現不了server調用client。
解決方案:讓client 既能夠調用server上的RPC服務,反之client自己也成爲一個RPC服務讓Server來調用。

Netty和RPC
1. Netty只是網絡通訊框架,目的是讓你用最少的代碼構建出足夠支撐網絡通訊的功能。

2.完成RPC 須要兩個協議: 對象序列化協議 和 調用控制協議

常見例子舉例:

1.zeroC ICE,擁有本身的網絡通訊框架 + ICE 調用控制協議和對象序列化協議,同時也涵蓋了服務組件的抽象部署等功能。

2.thrift,有本身的網絡通訊框架+thrift 對象序列化協議+thrift 調用控制協議

3.probuff,只是 對象序列化協議

4.XMLRPC ,jsonRPC,常見的語境是利用HTTP協議做爲調用控制協議,XML 和 JSON 做爲對象序列化以後的格式。

5.其餘的大概也差很少了。
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息