什麼是gRPC
gRPC是Google開發的高性能、通用的開源RPC框架,其由Google主要面向移動應用開發並基於HTTP/2協議標準而設計,基於Protobuf(Protocol Buffers)序列化協議開發,且支持衆多開發語言。在gRPC中一個客戶端可以像使用本地對象那樣直接調用位於不同機器上的服務端應用的方法(methods)。這讓你能夠更容易的構建分佈式的應用和服務。和其他RPC
系統類似,gRPC
也是基於定義一個服務,指定服務可以被遠程調用的方法以及他們的參數和返回類型。在服務端,實現服務的接口然後運行一個gRPC
服務來處理可出端的請求。在客戶端,客戶端擁有一個存根(stub在某些語言中僅稱爲客戶端),提供與服務器相同的方法。
·gRPC
客戶端和服務器可以在各種環境中運行並相互通信,並且可以使用gRPC
支持的任何語言編寫。因此,例如,您可以使用Go,Python或Ruby的客戶端輕鬆地用Java創建gRPC
服務器。此外,最新的Google API的接口將擁有gRPC
版本,可讓您輕鬆地在應用程序中內置Google功能。
使用protocol buffer
默認情況下,gRPC使用protocol buffer,用於序列化結構化數據(儘管它可以與其他數據格式(例如JSON)一起使用)。使用協議緩衝區的第一步是在proto文件中爲要序列化的數據定義結構:proto文件擴展名爲.proto的普通文本文件。protocol buffer數據被構造爲消息,其中每個消息都是信息的邏輯記錄,其中包含一系列稱爲字段的名稱/值對。這是一個簡單的示例:
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
定義了數據結構後,就可以使用protocol buffer編譯器protoc
生成你所選語言的數據訪問類。訪問類爲每個字段提供了簡單的訪問器(例如name()
)和set_name()
),以及將整個結構序列化爲原始字節或從原始字節中解析出整個結構的方法-例如,如果您選擇的語言是C ++,則在上面的示例將生成一個名爲Person
的類。然後,您可以在應用程序中使用此類來填充,序列化和檢索Person
的protocol buffer消息。
除此之外你還要在.proto
件中定義gRPC服務,並將RPC方法參數和返回類型指定爲protocol buffer消息:
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
gRPC使用也是使用編譯器protoc
從proto文件生成代碼,不過編譯器要首先安裝一個gRPC插件。使用gRPC插件,你可以獲得生成的gRPC客戶端和服務器代碼,以及用於填充,序列化和檢索消息類型的常規protocol buffer訪問類代碼。
下面會更詳細地介紹gRPC裏的一些關鍵的概念。
服務定義
與許多RPC系統一樣,gRPC圍繞定義服務的思想,指定可通過其參數和返回類型遠程調用的方法。默認情況下,gRPC使用protocol buffer作爲接口定義語言(IDL)來描述服務接口和有效負載消息的結構。如果需要,可以使用其他替代方法。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPC允許定義四種服務方法:
- 一元RPC,客戶端向服務器發送單個請求並獲得單個響應,就像普通函數調用一樣。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- 服務器流式RPC,客戶端向服務器發送請求,並獲取流以讀取回一系列消息。客戶端從返回的流中讀取,直到沒有更多消息爲止。 gRPC保證單個RPC調用中的消息順序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- 客戶端流式RPC,客戶端使用提供的流寫入消息序列然後將它們發送到服務器。客戶端寫完消息後,它將等待服務器讀取消息並返回響應。 gRPC保證了在單個RPC調用中的消息順序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 雙向流式RPC,雙方都使用讀寫流發送一系列消息。這兩個流是獨立運行的,因此客戶端和服務器可以按照自己喜歡的順序進行讀寫:例如,服務器可以在寫響應之前等待接收完所有客戶端消息,或者可以先讀取一條消息再寫入一條消息,或其他一些讀寫組合。每個流中的消息順序都會保留。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
在下面的RPC生命週期章節我們會更詳細的比較這幾種不同的RPC。
使用API界面
從.proto
文件中的服務定義開始,gRPC提供了protocol buffer編譯器插件,插件可生成客戶端和服務器端代碼。 gRPC用戶通常在客戶端調用這些API,並在服務器端實現相應的API。
- 在服務側,服務器實現服務中聲明的方法並運行一個gRPC服務器來處理客戶端的調用。gRPC的基礎設施解碼傳入的請求,執行服務的方法,編碼服務的響應。
- 在客戶端,客戶端擁有一個名爲stub(存根)的本地對象(在有些語言中更傾向於把stub叫做客戶端)該對象同樣實現了服務中方法。客戶端可以只在本地對象上調用這些方法,將調用參數包裝在適當的protocol buffer消息類型中,gRPC會負責將請求發送給服務器並且返回服務端的protocol buffer響應。
同步vs異步
同步RPC調用會阻塞當前線程直到服務器收到響應爲止,這是最接近RPC所追求的過程調用抽象的近似方法。另一方面,網絡本質上是異步的,並且在許多情況下能夠啓動RPC而不阻塞當前線程很有用。
大多數語言中的gRPC編程界面都有同步和異步兩種形式。可以在每種語言的教程和參考文檔中找到更多信息。
RPC生命週期
現在讓我們具體看一下當一個gRPC客戶端調用了一個gRPC服務器的方法後都發生了什麼。我們不會查看具體實現細節,留到後面的編程語言教程中再看實現細節。
一元RPC
首先來看一個最簡單的RPC類型,客戶端發送一個請求然後接受一個響應。
- 一旦客戶端調用了存根/客戶端對象上的方法,服務器會被通知RPC已經被調用了,同樣會接收到調用時客戶端的元數據、調用的方法名稱以及制定的截止時間(如果適用的話)。
- 然後,服務器可以立即發送自己的初始元數據(必須在發送任何響應之前發送),也可以等待客戶端的請求消息-哪個先發生應用程序指定的。
- 服務器收到客戶的請求消息後,它將完成創建和填充其響應所需的必要工作。然後將響應(如果成功)連同狀態詳細信息(狀態代碼和可選狀態消息)以及可選尾隨元數據一起返回。
- 如果狀態是OK,客戶端將獲得響應,從而在客戶端完成並終結整個調用過程。
服務器流式RPC
一個服務器流式RPC與簡單的一元RPC類似,不同的是服務器在接收到客戶端的請求消息後會發回一個響應流。在發送回所有的響應後,服務器的狀態詳情(狀態碼和可選的狀態信息)和可選的尾隨元數據會被髮回以完成服務端的工作。客戶端在接收到所有的服務器響應後即完成操作。
客戶端流式RPC
客戶端流式RPC也類似於一元PRC,不同之處在於客戶端向服務器發送請求流而不是單個請求。服務器通常在收到客戶端的所有請求後(但不一定)發送單個響應,以及其狀態詳細信息和可選的尾隨元數據。
雙向流式RPC
在雙向流式RPC中,調用再次由客戶端調用方法發起,服務器接收客戶端元數據,方法名稱和期限。同樣,服務器可以選擇發回其初始元數據,或等待客戶端開始發送請求。
接下來發生的情況取決於應用程序,因爲客戶端和服務器可以按任何順序進行讀取和寫入-流操作完全是獨立地運行。因此,例如,服務器可以等到收到所有客戶端的消息後再寫響應,或者服務器和客戶端可以玩“乒乓”:服務器收到請求,然後發回響應,然後客戶端發送基於響應的另一個請求,依此類推。
截止時間/超時時間
gRPC允許客戶端指定在RPC被DEADLINE_EXCEEDED
錯誤終結前願意等待多長時間來讓RPC完成工作。在服務器端,服務器可以查看一個特定的RPC是否超時或者還有多長時間剩餘來完成RPC。
如何指定期限或超時的方式因語言而異-例如,並非所有語言都有默認期限,某些語言API按照期限(固定的時間點)工作,而某些語言API根據超時來工作(持續時間)。
RPC終止
在gRPC中,客戶端和服務端對調用是否成功做出獨立的基於本地的決定,而且兩端的結論有可能不匹配。這意味着,比如說,你可能會有一個在服務端成功完成(“我已經發送完所有響應了”)但是在客戶端失敗(“響應是在我指定的deadline之後到達的”)的RPC。服務器也有可能在客戶端發送所有請求之前決定RPC完成了。
取消RPC
客戶端或服務器都可以隨時取消RPC。取消操作將立即終止RPC,因此不再進行任何工作。這不是“撤消”:取消之前所做的更改不會回滾。
元數據
元數據是以鍵值對列表形式提供的關於特定RPC調用的信息(比如說身份驗證詳情),其中鍵是字符串,值通常來說是字符串(但是也可以是二進制數據)。元數據對gRPC本身是不透明的-它允許客戶端向服務器提供與調用相關的信息,反之亦然。
對元數據的訪問取決於語言。
通道
一個gRPC通道提供了一個到指定主機和端口號的gRPC服務器的連接,它在創建客戶端存根(或者對某些語言來說就是“客戶端”)時被使用。客戶端可以指定通道參數來更改gRPC的默認行爲,比如說打開/關閉消息壓縮。每個通道都有狀態,狀態包括connected
和idle
(閒置)
gRPC怎麼處理關掉的通道是語言相關的,有些語言還允許查詢通道的狀態。