RPC 與 gRPC
核心定義
RPC(Remote Procedure Call)是一種 通訊模型:允許程式呼叫 遠端伺服器上的函式,像呼叫本地函式一樣。
核心理念
隱藏網路細節,讓遠端方法呼叫看起來像本地呼叫。
| 設計導向 | REST | GraphQL | RPC |
|---|---|---|---|
| 中心 | Resource | Query | Method(方法) |
| API 形式 | URI 操作 | Query 語言 | 直接呼叫 function |
| 典型用途 | 對外 API | 對外 API | 對內微服務通訊 |
核心特徵
1. 方法導向(Method-oriented)
API 以「動作」為中心,而非資源:
REST: GET /users/123
gRPC: userService.GetUser(id=123)
2. 跨語言協定支援
透過 IDL(Interface Definition Language) 定義介面:
syntax = "proto3";
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
}
message GetUserRequest {
int64 id = 1;
}
message GetUserResponse {
int64 id = 1;
string name = 2;
string email = 3;
}
從 .proto 生成 stub / client library,不同語言都能互相呼叫(Go ↔ Python ↔ Java)。
3. 高效序列化格式
| 格式 | 類型 | 範例大小 | 吞吐量 |
|---|---|---|---|
| JSON | 文字 | {"id":"123","name":"Terry"} = ~40 bytes |
1x(基準) |
| Protobuf | 二進位 | 同等內容 ≈ 15 bytes | ~10x |
| Thrift / Avro | 二進位 | 類似 Protobuf | ~10x |
為什麼 Protobuf 這麼快?
- 無欄位名稱(用 tag number 1, 2, 3...)
- 可變長度編碼(varint)— 小數字佔更少 byte
- Schema 事先知道 → 不需解析結構
4. 雙向串流(Bidirectional Streaming)
gRPC 基於 HTTP/2,支援四種呼叫模式:
| 模式 | Client → Server | Server → Client | 例子 |
|---|---|---|---|
| Unary | 1 | 1 | 一般 RPC call |
| Server streaming | 1 | N | 訂閱事件 |
| Client streaming | N | 1 | 上傳大量資料 |
| Bidirectional | N | N | 聊天室、即時協作 |
gRPC 使用流程
1. 定義 .proto 2. 執行 protoc 生成 stub
┌──────────────┐ ┌──────────────────┐
│ user.proto │ ──protoc──► │ user_pb2.py │
│ service ... │ │ user_pb2_grpc.py │
└──────────────┘ │ user.pb.go │
│ User.java │
└──────────────────┘
3. Client 呼叫(像本地函式)
┌────────────────────────────────────────┐
│ user = client.GetUser(GetUserRequest( │
│ id=123 │
│ )) │
│ # 底層透過 HTTP/2 + Protobuf 傳輸 │
└────────────────────────────────────────┘
優點
| 優點 | 說明 |
|---|---|
| 呼叫直觀 | 對開發者像在呼叫本地函式 |
| 效能高 | 二進位序列化 + HTTP/2 多工 → 吞吐量 ~10x JSON/REST |
| 強型別 | IDL 提供清晰契約,跨語言型別安全 |
| 支援串流 | Bidirectional streaming 適合 real-time |
缺點
| 缺點 | 說明 |
|---|---|
| 耦合度高 | Client/Server 都依賴相同 IDL,版本演進需規劃 |
| 不如 REST 通用 | 需預先生成 stub;無法像 REST 用 curl 直接測試 |
| Debug 困難 | Protobuf 是二進位,不像 JSON 可讀 |
| 對外開放性差 | 瀏覽器原生不支援 gRPC(需要 gRPC-Web 代理) |
| 浏览器限制 | HTTP/2 trailers 在瀏覽器不開放,需轉譯層 |
RPC 版本演進
Protobuf 有設計規則避免破壞相容性:
message User {
int64 id = 1;
string name = 2;
// reserved 3; // 舊欄位刪除後保留 tag
string email = 4; // 新增欄位要用新 tag number
}
相容規則:
- 新增欄位 → 用新 tag number,舊 client 忽略未知欄位(forward compatible)
- 刪除欄位 → 保留
reservedtag,不可重用 - 改型別 → 通常破壞相容,需新 tag
面試中如何談 RPC
- 典型使用場景:
- 微服務之間的內部通訊 → gRPC 是業界標準
- 高效能、低延遲、大量呼叫
- 需要 streaming(聊天、監控、資料管道)
- 對外公開 API:通常用 REST/GraphQL(直觀、跨平台、好 debug)
- 對內服務間通訊:多選 RPC(效能好、IDL 保證一致性)
面試口訣
「REST/GraphQL 對外,gRPC 對內」 — 這是業界常見的混合策略。
陷阱:過早跳 gRPC
面試官要看你思考問題的過程。在沒有明確效能瓶頸前就選 gRPC,會顯得在「為了用而用」。
正確順序:先 REST → 確認內部服務有效能瓶頸 → 再提 gRPC。
Simplification-with-exceptions
邊界情況
- gRPC 不是唯一 RPC:還有 Thrift(Facebook)、Avro(Apache)、JSON-RPC、XML-RPC
- gRPC-Web:讓瀏覽器用 gRPC,但需代理(Envoy)做 HTTP/1 ↔ HTTP/2 轉換
- 對外也能用 gRPC:B2B 合作夥伴(雙方都是服務)可用 gRPC + mTLS
- Debug 工具在進步:grpcurl、BloomRPC、Postman 都支援 gRPC
Related Notes
- 03-API-Design/01-API-Design-Framework — Internal 直接選 RPC
- 03-API-Design/02-REST — 對外 API 主流
- 03-API-Design/03-GraphQL — 對外靈活查詢
- 03-API-Design/05-API-Security — mTLS 常搭配內部 gRPC
- 01-Networking/04-API-Paradigms(01-Networking)— 速覽版
- 03-API-Design/Practice-API-Design