最近項目需要用到 gRPC,網(wǎng)上gRPC 的資料較少,翻譯了官網(wǎng)的 gRPC guide 文檔,以供組內(nèi)學習,部分暫時用不到的部分未進行翻譯。
gRPC Guide
該文檔主要介紹 gRPC 和 protocol buffers。 gRPC 使用 protocol buffers 作為 IDL 和消息交換格式。本文檔適合與剛接觸 gRPC 或者 protocol buffers 的初學者。
概述
gRPC client 可以直接像調(diào)用本地方法一樣調(diào)用 server 的方法,這使得開發(fā)分布式應用和服務變得更加簡單。和很多其他 RPC 框架一樣,gRPC 通過指定方法調(diào)用的參數(shù)和返回值來定義服務。server 實現(xiàn) RPC 接口,處理客戶端的調(diào)用請求。client 提供和 server 一樣的接口。

gRPC client 和 server 可以在不同的環(huán)境運行和通信,比如 Google 內(nèi)部的服務器到你的桌面應用。client 和 server 的語言可以是任何 gRPC 支持的語言,你可以用 C++ 來實現(xiàn) server, 用 java 和 ruby 來實現(xiàn) client。另外最新的 Google API 都提供了 gRPC 接口,來幫助你在應用中更便捷的使用 Google 提供的功能。
Working with Protocl Buffers
gRPC 默認使用 Protocol buffers,這是一個 google 開源的成熟的數(shù)據(jù)序列化機制。接下來會簡單介紹一下它是如何工作的。
使用 protocol buffers 前需要先將你要序列化的結構化數(shù)據(jù)定義為 .proto 文件。protocol buffers 通過 messages 定義數(shù)據(jù)結構,每個 message 由一系列屬性組成,類似于類的定義,我們可以看一個簡單的例子
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
定義好數(shù)據(jù)結構后,可以使用 protocol buffers 編譯器(protoc)將其生成其他語言的類。這些類提供所有屬性的訪問方法,及序列化和反序列方法。例如,你使用的是 C++,運行編譯器會生成一個 Person 類,你可以在在你的應用中實例化或序列化來傳輸這些 protocol buffers 信息。
除了數(shù)據(jù)結構外,你還可以在 .proto 文件中定義 gRPC 服務,包括服務接口的參數(shù)和返回類型。例如:
// 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;
}
和正常使用 protocol buffers 不一樣, gRPC 可以使用 protoc 的 gRPC 插件生成代碼,除了會生成共傳播和序列化的類以外,還會生成 gRPC client 和 server 代碼。
protocol buffers 版本
建議使用 proto3 版本的 protocol buffers,語法更簡潔,支持更多語言。
基本概念
服務定義
之前已經(jīng)提到了,gRPC 的基于 protocol buffers 定義服務。所謂定義服務,就是使用 IDL 來描述你的服務接口和傳輸消息結構。gRPC 使用 protocol buffers 作為 IDL。
gRPC 支持四種類型的服務方法
- 簡單RPC,client 發(fā)送單個請求,server 返回單個響應,和普通的方法調(diào)用一樣。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- server 流式RPC, client 發(fā)送單個簡單請求,server 返回流,用于讀取一系列的消息。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- client 流式 RPC, client 通過流向 server 發(fā)送一些列的消息,當 client 發(fā)送完消息后,等待服務端讀取并返回響應
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 雙向的流式 RPC,client 的請求和 server 的響應都是流式的。注意這兩條流是獨立的,所以 server 可以邊讀請求邊返回響應,也可以將流讀完在返回響應。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
API 使用
gRPC 使用 .proto 生成 client 和 server 代碼及 API。client 調(diào)用這些 API, server 實現(xiàn)這些API。
- server 側實現(xiàn)服務聲明的方法,并運行 gRPC server 來處理 client 的調(diào)用。gRPC 幫助你反序列化請求,執(zhí)行服務方法,序列化響應并返回給 client.
- client 側 gRPC 會生成一個 client,這個 client 提供和 server 側一樣的接口供用戶調(diào)用,并且使用 protocol buffer 序列化用戶的請求參數(shù),發(fā)送給 server,然后反序列化 server 的響應。
同步 VS 異步
同步 RPC 調(diào)用在 server 的響應到達之前會阻塞,這是 RPC 的常用使用方式。另一方面,web 服務在很多場景下天然是異步的,因此異步的 RPC 也經(jīng)常使用,異步 RPC 調(diào)用不會阻塞當前線程。
gRPC 的同步和異步調(diào)用完全依賴用戶自身的代碼處理。
RPC life cycle
簡單 RPC
- 當 client 調(diào)用 RPC 方法時,會通知 server 對應的 RPC 方法被調(diào)用,包括 client 調(diào)用的元數(shù)據(jù)信息,方法名,以及 deadline
- server 也需要通知 client 自己的元數(shù)據(jù)(這步發(fā)生在所有響應之前),可以直接通知,也可以等收到客戶端第一個請求時發(fā)送
- 當 server 接到 client 的請求后,根據(jù)請求和業(yè)務邏輯生成響應,這些響應包括狀態(tài)碼及payload,并返回給 client
- 如果狀態(tài) OK, client 會收到響應,并在 client 側完成這次調(diào)用
流式 RPC
Deadline / timeout
gRPC 允許 client 設置過期時間,超過過期時間 RPC 會被中止,并返回 DEADLINE_EXCEEDED 錯誤。。同時 server 可以查看某個 RPC 是否超時,或者還有多長時間超時。
deadline 或者 timeout 在不同語言的 API 可能不一樣,不一定都提供, deadline = timeout + now。
RPC 中斷
在 gRPC 中,client 和 server 都可以獨自決定一次調(diào)用是否成功,并且結論可能不一致。這也就意味,server 可能認為 RPC 已經(jīng)成功了,但是 client 認為失敗了(比如超時)。甚至 server 可以在 client 還未將 request 完全發(fā)送完就結束一次調(diào)用。
Metadata
Metadata 元數(shù)據(jù)使用 kv 對表示某個特定 RPC 調(diào)用的信息,key 是字符串,values 通常也是字符串,也可以是二進制數(shù)據(jù)。元數(shù)據(jù)對于 gRPC 本身是不透明的,它提供了使 client 與 server 端調(diào)用關聯(lián)起來的信息。
通道 (Channels)
創(chuàng)建 client 時,gRPC 的通道提供了和某個特定 host 及 port 的 gRPC server 之間的連接。clietn 可以通過參數(shù)改變 gRPC 的默認欣慰,比如消息壓縮開關等。通道是有狀態(tài)的,包括 connected 和 idle。
如何處理關閉的通道不同語言不太一樣。有的語言甚至不提供通道狀態(tài)查詢。
鑒權
可通過 TSL 或 Token 的方式進行鑒權。
HTTP2
gRPC 是基于 HTTP2
Request:
HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
DATA (flags = END_STREAM)
<Delimited Message>
Response
HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
DATA
<Delimited Message>
HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc
錯誤處理
錯誤模型
我們之前看到的信息都是 server 返回狀態(tài)正常 OK 時的情況,如果調(diào)用不成功會發(fā)生什么呢?
當錯誤發(fā)生時,gRPC 會返回狀態(tài)碼,也可能會包括一些錯誤的描述信息。
錯誤狀態(tài)碼
gRPC 會在很多情況下產(chǎn)生錯誤,包括網(wǎng)絡失敗,鑒權失敗等,這些錯誤都會伴隨一個狀態(tài)碼,所有語言的狀態(tài)碼都是統(tǒng)一的。
通用錯誤
| 描述 | 狀態(tài)碼 |
|---|---|
| client 取消請求 | GRPC_STATUS_CANCELLED |
| 超時 | GRPC_STATUS_DEADLINE_EXCEEDED |
| server 端未找到對應方法 | GRPC_STATUS_UNIMPLEMENTED |
| server 已關閉 | GRPC_STATUS_UNAVAILABLE |
| server 內(nèi)部錯誤 | GRPC_STATUS_UNKNOWN |
網(wǎng)絡錯誤
| 描述 | 狀態(tài)碼 |
|---|---|
| 在deadline 之前沒有數(shù)據(jù)傳回 | GRPC_STATUS_DEADLINE_EXCEEDED |
| 在連接破壞前,有一部分數(shù)據(jù)傳回 | GRPC_STATUS_UNAVAILABLE |
協(xié)議錯誤
| 描述 | 狀態(tài)碼 |
|---|---|
| 壓縮算法不支持 | GRPC_STATUS_INTERNAL |
| client 使用的壓縮機制 server 不支持 | GRPC_STATUS_UNIMPLEMENTED |
| 流量超過限制 | GRPC_STATUS_RESOURCE_EXHAUSTED |
| 違反流量控制協(xié)議 | GRPC_STATUS_INTERNAL |
| 未知的返回狀態(tài) | GRPC_STATUS_UNKNOWN |
| 未授權 | GRPC_STATUS_UNAUTHENTICATED |
| protocol buffer 解析失敗 | GRPC_STATUS_INTERNAL |