grpc是rpc框架的一種,定義了遠程方法調用的方式。最近總結學習了一些關於grpc的知識,從rpc開始切入,寫下這篇文章。
rpc是遠程過程調用(Remote Procedure Call,縮寫為 RPC)。是一種計算機通信協議,該協議允許運行於一台計算機的程序調用另一台計算機的子程序,而程序員無需額外地為這個交互作用編程。 如果涉及的軟件采用面向對象編程,那么遠程過程調用亦可稱作遠程調用或遠程方法調用
上圖為rpc調用過程, RPC 本身是 client-server模型,也是一種 request-response 協議。不同的rpc框架本質都是客戶端發起請求,服務器作出響應。
服務的調用過程為:
- client調用client stub,這是一次本地過程調用
- client stub將參數打包成一個消息,然后發送這個消息。打包過程也叫做 marshalling
- client所在的系統將消息發送給server
- server的的系統將收到的包傳給server stub
- server stub解包得到參數。 解包也被稱作 unmarshalling
最后server stub調用服務過程. 返回結果按照相反的步驟傳給client
RPC只是描繪了 Client 與 Server 之間的點對點調用流程,包括 stub、通信、RPC 消息解析等部分,在實際應用中,還需要考慮服務的高可用、負載均衡等問題,所以產品級的 RPC 框架除了點對點的 RPC 協議的具體實現外,還應包括服務的發現與注銷、提供服務的多台 Server 的負載均衡、服務的高可用等更多的功能。 目前的 RPC 框架大致有兩種不同的側重方向,一種偏重於服務治理,另一種偏重於跨語言調用。
gRPC
gRPC就是一種偏向於跨語言調用的rpc框架,由Google開源。
概念
一個客戶端程序可以直接調用一個服務端程序中的方法,這個服務端程序可以是在另外一台機器上,調用起來就像它是一個本地對象,這就方便你創造分布式的應用和服務。
正如需要RPC系統那樣,gRPC的思想基礎也是:定義一個服務,然后指定方法可以通過參數和返回類型,來遠程調用它們。在服務端,程序實現這個接口,並且運行一個gRPC服務器來處理客戶端調用。在客戶端,有一個存根(stub,即為用任何語言寫的客戶端程序),它提供了和服務端相同的方法。
數據格式
通信過程需要定義一種數據格式,比如json、xml等都是一種數據格式。grpc使用proto buffer,用來序列化結構化的數據。proto buffer也是Google的一個成熟開源框架。
使用proto buffer,先定義.proto后綴的proto文件。proto buffer的數據被結構化為消息,每一個message都是一個小的邏輯記錄,包含了一系列的被稱為fields的name-value對信息。例如proto文件定義如下。
1 |
message Person { |
定義好proto文件后,使用protoc
編輯器。生成對應編程語言的類文件如Python、Java和Golang等。然后就可以使用這個類反序列化和序列化進行grpc通信。
1 |
// The greeter service definition. |
gRPC還使用protoc
和一個特殊的gRPC插件,從proto文件中生成代碼。並且,通過gRPC插件,你可以得到生成的gRPC客戶端和服務端代碼,就像上面那樣用來填充,序列化,和接收消息類型的規則protocol buffer代碼一樣。
gRPC通信方法
一元RPC
這是最簡單的RPC類型,客戶端向服務器發送一個單獨請求,並且獲取一個單一響應,就像一個普通的方法調用。
當客戶端調用本地對象stub上的方法時,服務端就會收到通知說RPC已經被調用,包含有這次調用的客戶端元數據(metadata),方法名,還有指定的期限(deadline)如果可用。
服務器可以選擇直接發回它自己的初始元數據(必須在任何響應之前發送),或者等待客戶端的請求消息 – 先發生那種情況,由應用程序指定。
一旦服務器有了客戶端的請求消息,它就開始進行產生並填充響應的相關工作。響應隨即被返回(如果成功)至客戶端,包含了狀態詳情(狀態碼,和可選的狀態消息),以及可選的拖尾元數據(trailing metadata)
如果狀態是OK,客戶端會得到響應,然后由客戶端這邊完成本次調用。
定義方式:
1
2rpc SayHello(HelloRequest) returns (HelloResponse){
}
服務端流式RPC
一個服務端流式RPC和我們的簡單樣例很相似,除了服務端在收到客戶端請求消息后,返回一個響應流。當返回完它的所有響應后,服務端的狀態詳情(狀態碼,和可選的狀態消息)以及可選的拖尾元數據會被返回,以此完成服務端部分的工作。而客戶端一旦得到服務器的所有響應就會完成本次調用。
定義方式
1 |
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){ |
客戶端流式RPC
一個客戶端流式RPC也和我們的例子很相似,除了客戶端向服務器發送一個請求流,而不是一個單獨請求。服務器返回一個單獨響應,當服務器獲取到客戶端所有請求后,它一般都會在響應中,附帶上狀態詳情和可選的拖尾元數據,但這不是必需的。
定義方式
1 |
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) { |
雙向流式RPC
在雙向流式RPC中,也同樣是客戶端發起通信,調用方法,並且服務端接收客戶端元數據,方法名,和期限。服務端也同樣是可以選擇發回它的初始元數據,或者是等待客戶端開始發送請求。
接下來發生什么取決於應用程序,由於客戶端和服務器可以以任意順序進行讀寫 – 流操作是完全獨立的。所以,舉個例子,服務器可以等待直到它收到客戶端的所有消息,然后開始寫響應,或者服務器和客戶端以打乒乓球的方式交流:服務器收到一個請求,然后發回一個響應,然后客戶端基於響應再發起其它請求,以此類推
1 |
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){ |