Skip to content

gRPC 是甚麼? #71

@ChaoLiou

Description

@ChaoLiou

grpc.io

gRPC 介紹

gRPC 和 protocol buffers 介紹

  • gRPC 可以使用 protocol buffers 當作兩邊的 Interface Definition Language (IDL, 介面描述語言) 和底層的訊息交換格式.

概觀

  • 在 gRPC, client app 可以直接呼叫不同機器上 server app 的 method, 彷彿就是一個 local object, 讓你更容易建立出分散式的 app 和 services. 如同很多 RPC 系統, gRPC 的基礎是圍繞在 定義一個 service, 指定可以遠端被呼叫的 method, 與其參數和回傳型別. 在 server 端, server 實作這個 interface, 並運行一個 gRPC server 處理 client 的呼叫. 在 client 端, client 有一個 stub (在某些語言中, 就稱作 client), 他提供和 server 一樣的 methods.

  • gRPC clients 和 servers 可以在多樣的環境中運行, 並彼此溝通 - 從 Google 的 servers 到 你自己的桌機 - 而且能以 gRPC 支援的語言寫出來. 所以, 舉例來說, 你能以 Java 簡單地建立一個 gRPC server, 而 clients 則用 Go, Python 或 Ruby. 此外, 最新的 Google APIs 會有他們 interfaces 的 gRPC 版本, 讓你輕鬆將 Google 功能建置在你的 app 裡面.

使用 Protocol Buffers

  • gRPC 預設使用 Protocol Buffers, 他是 Google 成熟的 open source, 用於 serializing structured data(序列化具結構的資料) 的機制, 雖然也是可以用其他種資料格式, 像是 JSON.
  • 當使用 protocol buffers 時, 第一步是為你要序列化的資料, 在一個 proto 檔 中定義出結構: 這是一個普通的 text 檔, 帶有一個 .proto 副檔名. Protocol buffer 的資料的結構為 message, 而每則訊息都是一個微小的資訊邏輯紀錄, 內含一串連續的 name-value pairs, 稱作 fields:
message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}
  • 然後, 一旦指定你的資料結構, 使用 protocol buffer compiler proroc 從你的 proto 定義中, 產生你偏愛語言的 data access classes. 這些提供了簡單的 accessors, 每個 field 像是 name(), 每個 method 像是 set_name(), 再將整個結構 serialize 到 raw bytes, 或從 raw bytes 中將整個結構 parse 出來. 所以, 舉例來說, 如果你選的語言是 C++, 在上述的範例上運行 compiler, 會產生一個叫做 Person 的 class. 然後你可以在你的 app 使用這個 class, 對 Person 的 protocol buffer messages 做 populate, serialize 以及 retrieve.
  • 你在普通的 proto 檔中, 以 RPC method 參數和回傳型別, 定義 gRPC:
// greeter service 的定義.
service Greeter {
  // 送出一則 greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// request message 內含用戶名稱.
message HelloRequest {
  string name = 1;
}

// response message 內含 greetings
message HelloReply {
  string message = 1;
}
  • gRPC 使用一個特殊的 gRPC plugin, protoc 從你的 proto 檔中產生出程式碼: 你將得到產出的 gRPC client 和 server 的程式碼, 以及正規的 protocol buffer 程式碼, 用來 populating, serializing 和 retrieving 你的 message types.

Protocol buffer 版本

  • 雖然 protocol buffers 已提供 open source 給用戶一段時間, 大部分的範例使用 protocol buffers 版本 3 (proto3), 具備稍微簡化的語法, 和一些好用的新功能, 並支援更多語言.
  • 一般來說, 雖然你可以使用 proto2 (目前預設的 protocol buffers 版本), 但我們建議你一起使用 proto3 的 gRPC, 因為他讓你使用完整 gRPC 所支援的語言, 也能避免相容性問題, 像是 proto2 clients 與 proto3 servers 或 反過來, 的溝通問題.

核心概念, 架構 和 lifecycle

介紹 gRPC 的關鍵概念, 並概觀 gRPC 的架構 和 RPC lifecycle .

概觀

Service 定義

  • 像是許多 RPC 系統一樣, gRPC 的基礎是圍繞著 定義一個 service, 指定能被呼叫的 methods, 以及其參數和回傳型別. gRPC 預設使用 protocol buffers 當作 Interface Definition Language (IDL, 介面描述語言), 用來描述 service 介面 和 payload messages 的結構.
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}
  • gRPC 讓你定義四種 service method:
    • Unary RPCs 是 client 將單一 request 送到 server 並取回單一 response, 就像是一個正常的 function call.
rpc SayHello(HelloRequest) returns (HelloResponse);
  • Server streaming RPCs 是 clients 將單一 request 送到 server 並取回一段 stream, 並讀取一連串的 messages. client 從回傳的 stream 中讀取, 直到沒有 messages. gRPC 保證一筆個別的 RPC call 之間的 messages 順序.
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  • Client streaming RPCs 是 clients 寫入一連串 messages 並送到 server, server 再使用提供的 stream. 一旦 client 已完成寫入 messages, 等到 server 讀取並回傳其 response. gRPC 保證一筆個別的 RPC call 之間的 messages 順序.
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  • Bidirectional streaming RPCs 是兩邊都使用 read-write stream 送出一連串 messages. 兩個 streams 各自獨立操作, 所以 clients 和 servers 能以他們想要的任何順序讀取和寫入: 舉例來說, server 在寫入他的 responses 前, 會等到收完所有 client 的 messages, 或是他可以交替讀取一則 message 然後寫入一則 message, 或是其他讀取和寫入的組合. 每個 stream 的 messages 順序會保留住.
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

使用 API

  • 從一個 .proto 檔中的 service 定義開始, gRPC 提供 protocol buffer compiler plugins, 他產生 client 和 server 端的程式碼. gRPC 用戶通常在 client 端呼叫這些 APIs, 並在 server 端實作對應的 API.
    • 在 server 端, server 實作出由 service 宣告的 methods, 並運行一個 gRPC server 處理 client 呼叫. gRPC 的基礎設施將進來的request 解碼, 執行 service methods, 和將 service responses 編碼.
    • 在 client 端, client 有一個 local object, 稱作 stub (對某些語言來說, 偏好的詞是 client), 他實作出與 service 相同的 methods. 然後 client 就可以在 local object 上呼叫那些 methods, 將呼叫的參數包裝成適當的 protocol buffer message type - gRPC 會看管要送到 server 的 request(s), 並回傳 server 的 protocol buffer response(s).

Synchronous vs. asynchronous

  • Synchronous RPC 的呼叫 (block 直到有一個 response 從 server 抵達), 是最接近 RPC 渴望的一個 procedure call. 另一方面, 網路本質上是 asynchronous, 並且在很多情境中, 能夠在不會 block 目前 thread 的前提下啟動 RPCs 是很有幫助的.

RPC lifecycle

Unary RPC

  • 先研究最簡單的 RPC 類型, 是 client 將單一 request 送出並取回單一 response.
    1. 一旦 client 呼叫一個 stub method, server 會被通知 RPC 已經被呼叫, 帶著這次呼叫的 client metadata, method 名, 和 指定的 deadline(如果可以用).
    2. 然後 server 無論是直接送回他自己初始的 metadata (必須在任何 response 以前送出), 還是等待 client 的 request message.
    3. 一旦 server 有了 client 的 request message, 他會做好 create 與 populate 一個 response 所需要的一切工作. 然後 response 就被回傳 (如果成功) 到 client, 並帶著 status details (status code 和 status message)跟在後面的 metadata.
    4. 如果 response status 是 OK, client 得到 response, 也就是 client 端的呼叫完畢.

Server streaming RPC

  • 一個 server-streaming RPC一個 unary RPC 類似, 除了 server 回傳一個 messages stream 對應到 client 的 request. 在送出他所有的 message 後, server 的 status details (status code 和 status message)跟在後面的 metadata, 會被送到 client. 這會在 server 端完成處理流程. 一旦 client 有了所有 server 的 message, client 的呼叫就完畢了.

Client Streaming RPC

  • 一個 client-streaming RPC一個 unary RPC 類似, 除了 client 送出一個 messages stream 到 server, 而不是單一 message. 然後 server 回應單一 message (與其 status details跟在後面的 metadata 一起), 通常但不一定, 是在收到所有 client 的 messages 後回應.

Bidirectional streaming RPC

  • 在一個 bidirectional streaming RPC 中, 會因 client 呼叫 methodserver 收到 client 的 metadata, method 名, deadline 而初始化. server 可以選擇送回他初始的 metadata 或 等待 client 開始 streaming messages.
  • Client 和 server 端的 stream 處理是由 app 指定. 因為兩個 streams 是獨立的, client 和 server 能以任何順序讀取和寫入 messages, 或是 server 和 client 可以玩 "ping-pong" - server 得到一個 request, 然後送回一個 response, 然後 client 送另一個由這次 response 而定的 request, 等等.

Deadlines/Timeouts

  • gRPC 允許 clients 指定願意等待一個 RPC 多久, 不然就終止並回傳一個 DEADLINE_ECEEDED error. 在 server 端, server 可以查詢特定的 RPC 是否 timed out, 或是還剩多久 RPC 可以完畢.
  • 要指定 deadline 還是 timeout 是取決於語言: 有些語言的 APIs 對 timeouts (時間長短) 有效, 有些語言的 APIs 對 deadline (固定的時間點) 有效, 且可能有預設 deadline.

RPC 終止

  • 在 gRPC, client 和 server 都會做出獨立, local, 呼叫是否成功的決定, 他們的結論可能互不相符. 這意旨, 舉例來說, 你會有一個 RPC 在 server 端成功完成 ("我已經送出我所有的 responses!") 但在 client 端失敗 ("responses 在我的 deadline 後才抵達!"). 對 server 來說, 他也可能決定在 client 送出他所有的 requests 前完畢呼叫.

取消一個 RPC

  • 無論 client 還是 server 可以在任何時間取消一個 RPC. 一個 cancellation 會立即終止 RPC, 這樣就沒有進一步的工作可以達成.

在 cancellation 前的改動不能 rolled back.

Metadata

  • Metadata 是關於一個特定 RPC 呼叫的資訊, 是一列 key-value pairs 格式, keys 是 strings 而 values 通常是 strings, 但可以是 binary data. Metadata 對 gRPC 自己並不透明 - 他讓 client 提供呼叫到 server 有關的資訊, 反之亦然.
  • 存取 metatdata 取決於語言.

Channels

  • 一個 gRPC channel 提供一個連線, 在指定的 host 和 port, 連到 gRPC server. 當創建一個 client stub 時會使用到. Clients 可以指定 channel 的參數, 修改 gRPC 的預設行為, 像是切換 message compression 開關. channel 會有狀態, 包括 connectedidle.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions