結論先講

對外用 REST(或 GraphQL),對內用 gRPC。 前端打後端用 REST/GraphQL——因為瀏覽器原生支援、工具鏈成熟、debug 容易。微服務之間用 gRPC——因為 Protobuf 比 JSON 小 50% 快 30%(第 22 篇 壓測數據),而且有 schema 強制驗證。但如果你的微服務只有 3-5 個,REST 就夠了,不需要為了 30% 的效能上 gRPC。


四種協定一覽

協定格式傳輸方向適合
RESTJSONHTTP/1.1 or 2Request-Response對外 API、CRUD
gRPCProtobufHTTP/2雙向 Streaming服務間通訊
GraphQLJSONHTTP/1.1 or 2Request-Response前端彈性查詢
WebSocket/SSE自定 / JSONTCP / HTTP持久連線即時通訊、推送

REST:最簡單,大部分場景夠用

優點

  • 所有人都會:每個後端框架原生支援
  • 工具鏈最成熟:Postman、Swagger、curl
  • Debug 容易:看 HTTP status code + JSON body 就知道問題
  • 瀏覽器原生支援:fetch / axios 直接打

缺點

  • Over-fetchingGET /users/123 回了 30 個欄位,前端只要 3 個
  • Under-fetching:要拿用戶 + 訂單 + 商品,要打 3 個 API
  • JSON 效率低:文字格式,比 binary 大 2-3 倍
  • 沒有 schema 強制:API 改了 response 格式,client 不知道(直到 runtime 壞掉)

在微服務裡的使用

我們的系統就是用 REST(第 28 篇)。3-5 個微服務之間用 REST 通訊,簡單直接。

REST 在微服務的瓶頸不在「協定慢」,而在「序列化/反序列化」。 JSON.parse + JSON.stringify 在高流量下是有成本的。壓測數據:Protobuf 比 JSON 小 50%、快 30%。

什麼時候 REST 不夠

  • 微服務 > 10 個,服務間呼叫鏈很長(A → B → C → D),每一層的 JSON 序列化都是成本
  • 需要 streaming(上傳進度、即時資料流)
  • 前端查詢模式變化大,REST endpoint 爆炸

gRPC:微服務間通訊的最佳選擇

什麼是 gRPC

Google 開源的 RPC 框架。用 Protobuf 定義 schema,用 HTTP/2 傳輸,支援四種通訊模式。

// user.proto — schema 定義
syntax = "proto3";
 
service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (stream User);  // server streaming
}
 
message GetUserRequest {
  string id = 1;
}
 
message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

編譯後自動產生各語言的 client/server code。schema 是合約——改了 .proto 檔,client 和 server 都會在編譯時發現不相容。

四種通訊模式

模式說明場景
Unary一問一答(和 REST 一樣)一般查詢
Server StreamingClient 問一次,Server 連續回多次即時報價、進度更新
Client StreamingClient 連續送,Server 最後回一次上傳大檔分片
Bidirectional雙向持續通訊聊天室、協作編輯

Server Streaming 可以取代 SSE,Bidirectional 可以取代 WebSocket——而且效能更好(Protobuf binary vs JSON text)。

壓測數據

格式大小速度
JSON基準基準
Protobuf-50%+30%

小 50% = 網路傳輸省一半。快 30% = 序列化/反序列化效率高。在微服務間高頻呼叫(每秒幾千次),這個差距很顯著。

缺點

  • 瀏覽器不支援原生 gRPC:前端要用 gRPC-Web(多一層 proxy)
  • Debug 困難:Protobuf 是 binary,curl 看不懂。需要用 grpcurl 或 Postman gRPC
  • 學習成本:.proto 語法、code generation、streaming 狀態管理
  • 生態比 REST 小:不是所有語言/框架都有好用的 gRPC library

什麼時候用 gRPC

微服務之間通訊:
├── < 5 個服務,呼叫頻率低 → REST(簡單就好)
├── > 5 個服務,呼叫鏈長 → gRPC(省序列化成本)
├── 需要 streaming → gRPC(原生支援)
└── 需要 schema 強制 → gRPC(.proto 是合約)

GraphQL:前端的彈性查詢

解決的問題

REST 的 Over-fetching 和 Under-fetching:

# 一個 query 拿到用戶 + 最近訂單 + 訂單商品
# REST 要打 3 個 API,GraphQL 一次搞定
query {
  user(id: "123") {
    name
    email
    orders(last: 5) {
      id
      total
      products {
        name
        price
      }
    }
  }
}

優點

  • 前端自己決定要什麼欄位:不需要後端改 API
  • 一次拿到多個資源:減少 HTTP roundtrip
  • 強型別 schema:前端可以自動生成 TypeScript type

缺點和陷阱

1. N+1 查詢問題

上面的 query 如果 naive 實作,會觸發:

  • 1 次查 user
  • 1 次查 orders(5 筆)
  • 5 次查 products(每筆訂單各查一次) = 7 次 DB 查詢

要用 DataLoader 做 batching 才能避免。

2. 快取困難

REST 的 GET /users/123 可以用 HTTP cache。GraphQL 的 POST body 每次不同,HTTP cache 不管用。需要 Apollo Client 的 normalized cache 或自己處理。

3. 在微服務裡的複雜度

每個微服務有自己的 GraphQL schema → 需要 schema stitching 或 Apollo Federation 合併 → 多一層複雜度。

什麼時候用 GraphQL

  • 前端需要高度彈性的查詢(不同頁面要不同的欄位組合)
  • 多個前端共用同一個 API(Web、iOS、Android 各自需要不同欄位)
  • 團隊有 GraphQL 經驗

什麼時候不用

  • CRUD 為主的後台管理系統(REST 就夠了)
  • 微服務間通訊(用 gRPC,不用 GraphQL)
  • 團隊沒有 GraphQL 經驗(學習成本高)

混合使用才是正解

大部分成熟的微服務架構不會只用一種協定:

前端 ←→ API Gateway:     REST 或 GraphQL
API Gateway ←→ 微服務:   gRPC
微服務 ←→ 微服務:        gRPC
即時推送:                 WebSocket 或 gRPC Streaming
事件通知:                 Kafka/RabbitMQ(不是 HTTP)

我們的做法

目前用 REST(因為團隊 Python 背景,FastAPI 的 REST 支援最好)。如果服務數量成長到 10+ 且服務間呼叫頻率高,會在核心路徑上加 gRPC。

前端仍然用 REST——因為加 GraphQL 的 ROI 在我們的場景下不夠高(主要是 CRUD,不是複雜的嵌套查詢)。


選型速查

場景推薦
前端打後端(CRUD 為主)REST
前端打後端(查詢彈性大)GraphQL
微服務間(< 5 個服務)REST
微服務間(> 5 個、高頻)gRPC
即時推送(server → client)SSE 或 gRPC Server Streaming
即時雙向(聊天、協作)WebSocket 或 gRPC Bidirectional
事件通知(非同步)Kafka / RabbitMQ


下一篇

Race Condition 與分散式鎖 — 通訊方式選好了,接下來看併發控制——微服務裡兩個人同時搶最後一件商品怎麼辦。

本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:基於 ELK 的推薦系統:能做嗎?該怎麼規劃? → 下一篇:Race Condition 與分散式鎖:微服務裡的搶購怎麼不超賣