RPC是什麼?

目錄
一、什麼是RPC?
二、典型RPC調用框架
三、Thrift框架介紹
一、什麼是RPC?
(1)RPC(remote procedure call):遠程調用過程。php

服務器A部署應用a,服務器B部署應用b,當A服務器調用B服務器上的b應用的函數或者方法時,由於不在同一內存空間,不能直接調用,必須經過網絡來表達調用的語義傳達調用的數據。

既然是調用B機器上的服務,那A機器本身也建立一個這個服務不就也能夠調用了麼。原理上是能夠這麼作,可是隨着計算機的橫向發展,集羣的出現,使得多臺機器部署成一個集羣來對外提供服務。沒法在一個進程內甚至同一臺計算機上完成的需求就得須要RPC來實現了。java

RPC的框架不少,好比最先的額CORBA,Java RMI,Web Service的RPC風格,Hessian,Thrift,甚至是Rest API。apache

(2) 本地過程調用過程服務器

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

1 int Multiply(int l, int r) {網絡

2 int y = l * r;併發

3 return y;框架

4 }socket

5ide

6 int lvalue = 10;函數

7 int rvalue = 20;

8 int l_times_r = Multiply(lvalue, rvalue);

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

將 lvalue 和 rvalue 的值壓棧

進入Multiply函數,取出棧中的值10 和 20,將其賦予 l 和 r

執行第2行代碼,計算 l * r ,並將結果存在 y

將 y 的值壓棧,而後從Multiply返回

第8行,從棧中取出返回值 200 ,並賦值給 l_times_r

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

(3)遠程過程調用帶來的新問題

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

Call ID映射。咱們怎麼告訴遠程機器咱們要調用Multiply,而不是Add或者FooBar呢?在本地調用中,函數體是直接經過函數指針來指定的,咱們調用Multiply,編譯器就自動幫咱們調用它相應的函數指針。可是在遠程調用中,函數指針是不行的,由於兩個進程的地址空間是徹底不同的。因此,在RPC中,全部的函數都必須有本身的一個ID。這個ID在全部進程中都是惟一肯定的。客戶端在作遠程過程調用時,必須附上這個ID。而後咱們還須要在客戶端和服務端分別維護一個 {函數 <--> Call ID} 的對應表。二者的表不必定須要徹底相同,但相同的函數對應的Call ID必須相同。當客戶端須要進行遠程調用時,它就查一下這個表,找出相應的Call ID,而後把它傳給服務端,服務端也經過查表,來肯定客戶端須要調用的函數,而後執行相應函數的代碼。

序列化和反序列化。客戶端怎麼把參數值傳給遠程的函數呢?在本地調用中,咱們只須要把參數壓到棧裏,而後讓函數本身去棧裏讀就行。可是在遠程過程調用時,客戶端跟服務端是不一樣的進程,不能經過內存來傳遞參數。甚至有時候客戶端和服務端使用的都不是同一種語言(好比服務端用C++,客戶端用Java或者Python)。這時候就須要客戶端把參數先轉成一個字節流,傳給服務端後,再把字節流轉成本身能讀取的格式。這個過程叫序列化和反序列化。同理,從服務端返回的值也須要序列化反序列化的過程。

網絡傳輸。遠程調用每每用在網絡上,客戶端和服務端是經過網絡鏈接的。全部的數據都須要經過網絡傳輸,所以就須要有一個網絡傳輸層。網絡傳輸層須要把Call ID和序列化後的參數字節流傳給服務端,而後再把序列化後的調用結果傳回客戶端。只要能完成這二者的,均可以做爲傳輸層使用。所以,它所使用的協議實際上是不限的,能完成傳輸就行。儘管大部分RPC框架都使用TCP協議,但其實UDP也能夠,而gRPC乾脆就用了HTTP2。Java的Netty也屬於這層的東西。

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

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

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

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

二、典型RPC調用框架

RPC的實現和調用框架不少,簡單介紹其中幾種比較典型的:

RMI(RemoteManagementInterface),利用java.rmi包實現,基於Java遠程方法協議(Java Remote Method Protocol) 和java的原生序列化。

Hessian,是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 基於HTTP協議,採用二進制編解碼。

protobuf-rpc-pro,是一個Java類庫,提供了基於 Google 的 Protocol Buffers 協議的遠程方法調用的框架。基於 Netty 底層的 NIO 技術。支持 TCP 重用/ keep-alive、SSL加密、RPC 調用取消操做、嵌入式日誌等能。

Thrift,是一種可伸縮的跨語言服務的軟件框架。它擁有功能強大的代碼生成引擎,無縫地支持C + +,C#,Java,Python和PHP和Ruby。thrift容許你定義一個描述文件,描述數據類型和服務接口。依據該文件,編譯器方便地生成RPC客戶端和服務器通訊代碼。最初由facebook開發用作系統內個語言之間的RPC通訊,2007年由facebook貢獻到apache基金 ,如今是apache下的opensource之一 。支持多種語言之間的RPC方式的通訊:php語言client能夠構造一個對象,調用相應的服務方法來調用java語言的服務,跨越語言的C/S RPC調用。底層通信基於SOCKET。

Avro,出自Hadoop之父Doug Cutting, 在Thrift已經至關流行的狀況下推出Avro的目標不只是提供一套相似Thrift的通信中間件,更是要創建一個新的,標準性的雲計算的數據交換和存儲的Protocol。支持HTTP,TCP兩種協議。

三、Thrift框架介紹
最多見的RPC工具是Facebook開源的Thrift RPC框架。

Thrift是一個跨語言的服務部署框架,最初由Facebook於2007年開發,2008年進入Apache開源項目。Thrift經過一箇中間語言(IDL, 接口定義語言)來定義RPC的接口和數據類型,而後經過一個編譯器生成不一樣語言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),並由生成的代碼負責RPC協議層和傳輸層的實現。

Thrift其實是實現了C/S模式,經過代碼生成工具將接口定義文件生成服務器端和客戶端代碼(能夠爲不一樣語言),從而實現服務端和客戶端跨語言的支持。用戶在Thirft描述文件中聲明本身的服務,這些服務通過編譯後會生成相應語言的代碼文件,而後用戶實現服務(客戶端調用服務,服務器端提服務)即可以了。其中protocol(協議層, 定義數據傳輸格式,能夠爲二進制或者XML等)和transport(傳輸層,定義數據傳輸方式,能夠爲TCP/IP傳輸,內存共享或者文件共享等)被用做運行時庫。

Thrift的協議棧以下圖所示:

重試

在Client和Server的最頂層都是用戶自定義的處理邏輯,也就是說用戶只須要編寫用戶邏輯,就能夠完成整套的RPC調用流程。用戶邏輯的下一層是Thrift自動生成的代碼,這些代碼主要用於結構化數據的解析,發送和接收,同時服務器端的自動生成代碼中還包含了RPC請求的轉發(Client的A調用轉發到Server A函數進行處理)。

協議棧的其餘模塊都是Thrift的運行時模塊:

底層IO模塊,負責實際的數據傳輸,包括Socket,文件,或者壓縮數據流等。

TTransport負責以字節流方式發送和接收Message,是底層IO模塊在Thrift框架中的實現,每個底層IO模塊都會有一個對應TTransport來負責Thrift的字節流(Byte Stream)數據在該IO模塊上的傳輸。例如TSocket對應Socket傳輸,TFileTransport對應文件傳輸。

TProtocol主要負責結構化數據組裝成Message,或者從Message結構中讀出結構化數據。TProtocol將一個有類型的數據轉化爲字節流以交給TTransport進行傳輸,或者從TTransport中讀取必定長度的字節數據轉化爲特定類型的數據。如int32會被TBinaryProtocol Encode爲一個四字節的字節數據,或者TBinaryProtocol從TTransport中取出四個字節的數據Decode爲int32。

TServer負責接收Client的請求,並將請求轉發到Processor進行處理。TServer主要任務就是高效的接受Client的請求,特別是在高併發請求的狀況下快速完成請求。

Processor(或者TProcessor)負責對Client的請求作出相應,包括RPC請求轉發,調用參數解析和用戶邏輯調用,返回值寫回等處理步驟。Processor是服務器端從Thrift框架轉入用戶邏輯的關鍵流程。Processor同時也負責向Message結構中寫入數據或者讀出數據。

Thrift的模塊設計很是好,在每個層次均可以根據本身的須要選擇合適的實現方式。同時也應該注意到Thrift目前的特性並非在全部的程序語言中都支持。例如C++實現中有TDenseProtocol沒有TTupleProtocol,而Java實現中有TTupleProtocol沒有TDenseProtocol。

相關文章
相關標籤/搜索