Section 1:REST 在微服務間通訊的問題
假設你有一個電商系統,Order Service 需要向 Payment Service 發起付款請求。 用 REST 的話,看起來很直觀:
REST 微服務通訊示意圖
能用,但在高頻次、多服務的微服務架構下,這個做法有四個根本問題:
效能瓶頸
JSON 序列化 / 反序列化開銷大,HTTP/1.1 每次請求都需要建立新的 TCP 連線(沒有多路複用),高流量下延遲顯著上升。
型別不安全
Payment Service 不知道 Order Service 傳來的 amount 是 float 還是 integer。JSON 是純文字,型別只存在於文件,不在程式碼裡。
版本管理困難
API 欄位一旦更改,需要手動同步更新雙方的 Model 定義,非常容易漏改、改錯,也難以追蹤 breaking change。
沒有原生 Streaming
如果要傳輸大量資料(例如批次處理結果)或即時推送(例如訂單狀態流),REST 的請求 - 回應模型非常不方便,只能靠 WebSocket 或 SSE 繞過。
gRPC 如何解決這些問題?
- 二進位格式(Protobuf):比 JSON 小 3–10 倍,序列化速度快 5–10 倍
- HTTP/2:單一連線多路複用,消除 HTTP/1.1 的連線開銷
- 強型別 Protobuf:從 .proto 檔自動生成雙語言的客戶端與伺服器 stub
- 原生 Streaming:四種 RPC 模式,覆蓋所有通訊需求
Section 2:Protocol Buffers(Protobuf)
Protobuf 是 gRPC 的 IDL(Interface Definition Language)。你先用 .proto 檔定義好資料結構與 Service 介面, 再用 protoc 編譯器自動生成各語言的 client / server 程式碼。 這份 .proto 就是雙方服務的「合約」,型別不一致會在編譯期就報錯。
JSON 格式(文字)
Protobuf 格式(二進位)
為什麼 field number 不能改? Protobuf 序列化時,欄位的識別碼是 field number 而非名稱。 如果你把 amount = 2 改成 total = 2, binary 層面完全沒有影響,舊客戶端依然可以解碼。 但如果你把 field number 從 2 改成 5,舊客戶端就再也找不到 amount 了——這是 breaking change。
Section 3:Node.js gRPC Server 實作
Node.js 使用 @grpc/grpc-js 套件實作 gRPC server。 不需要額外編譯 .proto,直接在 runtime 載入即可:
gRPC 錯誤狀態碼:gRPC 有自己的一套狀態碼系統(非 HTTP status),例如INTERNAL(5xx 類)、NOT_FOUND(404 類)、PERMISSION_DENIED(403 類)、DEADLINE_EXCEEDED(timeout)。 設計良好的 gRPC service 應該使用語意明確的狀態碼,而不是所有錯誤都回 INTERNAL。
Section 4:gRPC Client 實作
gRPC 的 client 使用同一份 .proto 檔生成,所以型別是保證對得上的。 原生 API 是 callback 風格,實際使用時通常 promisify 一下:
Section 5:gRPC 與 REST 詳細對比
gRPC 不是要取代 REST,而是針對不同場景各有優勢。看完這張表,你就知道什麼情況下該選哪個:
| 面向 | REST | gRPC |
|---|---|---|
| 資料格式 | JSON(文字) | Protobuf(二進位) |
| 傳輸協議 | HTTP/1.1 | HTTP/2 |
| 效能 | 基準 | 5–10x 更快 |
| 型別安全 | 無(需要 OpenAPI) | ✅ 強型別,編譯期檢查 |
| 客戶端生成 | 需要 openapi-generator | ✅ protoc 自動生成 |
| 瀏覽器支援 | ✅ 完整支援 | ❌ 需要 grpc-web proxy |
| Streaming | Long Polling / SSE | ✅ 原生四種 RPC 模式 |
| 學習成本 | 低 | 中(需要學 Protobuf) |
| 調試友好度 | ✅ curl / Postman 即可 | 需要 grpcurl / Postman gRPC |
效能差異從何而來?HTTP/2 的多路複用(Multiplexing)是關鍵——多個 RPC 呼叫可以共用同一條 TCP 連線, 消除了 HTTP/1.1 每次都要重新建立連線的開銷。加上 Protobuf 的二進位壓縮,在高流量微服務之間效果非常顯著。
HTTP/1.1 vs HTTP/2 連線模型
Section 6:什麼時候用 gRPC
工具沒有好壞,只有適不適合。實際工程決策應根據場景來選型:
用 gRPC
- 微服務間的內部通訊
- 高頻低延遲的 API 呼叫
- 需要 Streaming(即時推送、批次)
- 多語言環境(Go + Node.js + Python)
- API 合約需要強型別保障
用 REST
- 對外公開的 API(第三方整合)
- 瀏覽器直接呼叫(無 proxy)
- 簡單的 CRUD API
- 團隊熟悉度高、快速迭代
- 需要 CDN 快取(GET 請求)
用 tRPC
- Next.js 全端應用
- 整個 stack 都是 TypeScript
- 不需要跨語言通訊
- 開發速度優先(零 schema 定義)
- 小型到中型團隊
現實世界的架構選擇
Google、Netflix、Uber 等公司的微服務架構通常長這樣: 對外是 REST API(瀏覽器友好),服務與服務之間是 gRPC(效能最優)。 這兩者並不互斥,你可以用 API Gateway 把外部 REST 請求轉發給內部 gRPC 服務。