在現代軟體工程中,「如何定義系統間的通訊合約」是每個架構師必須面對的核心決策。
長久以來,REST(Representational State Transfer)一直是 Web 開發的事實標準;然而隨著微服務拆分帶來的內部超高頻 RPC 呼叫,gRPC 憑藉二進位序列化與 HTTP/2 雙向串流強勢崛起;與此同時,面對複雜的多端前端(Web、iOS、Android、IoT)與龐大的資料視圖聚合需求,GraphQL 又以「宣告式按需查詢」顛覆了傳統 API 的消費模式。
這三種技術並非簡單的替代關係,而是各自擁有截然不同的架構邊界與設計哲學。本文基於 gRPC 官方技術指南、GraphQL 官方規範 與 ByteByteGo System Design 101,建立一套工程化的選型決策模型。
gRPC (Protobuf / HTTP/2)
極致效能與強型別合約,二進位序列化節省 70% 體積,內部服務間低延遲 RPC 通訊首選。
REST (JSON / HTTP)
標準資源導向架構,人眼可讀、瀏覽器原生友好,具備最強的生態工具鏈與公開對外相容性。
GraphQL (宣告式圖查詢)
前端按需宣告欄位,單一請求聚合多服務資料;需搭配 DataLoader 解決後端 N+1 查詢與查詢複雜度熔斷。
1. gRPC:內部微服務極致吞吐與強型別合約
gRPC 由 Google 開源,核心建立在兩大支柱之上:Protocol Buffers(Protobuf) 二進位序列化協定與 HTTP/2 傳輸協定。
為什麼 gRPC 的效能比 REST 快 5 ~ 10 倍?
- 二進位緊湊編碼(Varint & Tag-Length-Value):
- REST 傳輸 JSON 時,每個鍵名(如
"user_id": 12345)都以 ASCII 字元傳送,浪費大量位元組。 - Protobuf 將欄位映射為整數編號(Tag),只傳輸二進位值與型別標記,Payload 體積平均縮小 60% ~ 80%。
- REST 傳輸 JSON 時,每個鍵名(如
- CPU 序列化/反序列化極快:JSON 解析需要大量字串掃描與記憶體分配;Protobuf 直接映射為記憶體結構體,CPU 消耗大幅降低。
- 原生雙向串流(Bi-directional Streaming):單一 TCP 連線上支援 Client Streaming、Server Streaming 與雙向全雙工串流,極度適合高頻遙測日誌與即時訊息。
syntax = "proto3";
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (OrderResponse);
rpc StreamOrderStatus (OrderLookup) returns (stream OrderStatusUpdate);
}
message CreateOrderRequest {
string user_id = 1;
repeated string item_ids = 2;
int64 timestamp = 3;
}
gRPC 的代價與邊界
- 瀏覽器支援度差:瀏覽器不允許 JavaScript 直接控制 HTTP/2 幀,需透過
grpc-web與 Envoy 代理轉發。 - 人眼不可讀:網路抓包(Wireshark)需要載入
.proto定義檔才能解碼,除錯門檻高於 JSON。
2. REST:公開生態、跨團隊協同與 HTTP 快取的王者
REST 誕生於 2000 年 Roy Fielding 的博士論文,強調**「以資源為核心(Resource-Oriented)」**,充分利用 HTTP 協定本身的語意。
REST 的核心架構優勢
- 天然的 HTTP 快取生態(Cache-Control & ETag):
- 瀏覽器、CDN(Cloudflare/Akamai)與反向代理完全理解 HTTP
GET與304 Not Modified,靜態與低頻數據可直接在邊緣快取,根本無需打到後端。
- 瀏覽器、CDN(Cloudflare/Akamai)與反向代理完全理解 HTTP
- 全球最大的開發者生態與工具鏈:Postman、cURL、Swagger/OpenAPI 等工具極度成熟,第三方開發者看文件 5 分鐘即可上手。
- 鬆偶合與無狀態(Statelessness):客戶端與伺服器各自獨立演進,透過標準 HTTP 動詞(GET, POST, PUT, DELETE)操作資源。
REST 遭遇的現代瓶頸
- Over-fetching(過度獲取):手機端只需要用戶姓名與頭像,但
/api/v1/users/123回傳包含 50 個欄位的龐大 JSON。 - Under-fetching 與瀑布請求(Waterfall Requests):渲染一個訂單詳情頁面,前端必須先請求
/orders,再遍歷每個 Order ID 發起/users、/payments、/products,造成嚴重的網路來回延遲(Round-trips)。
3. GraphQL:多端前端宣告式圖查詢與 N+1 陷阱
GraphQL 由 Meta(Facebook)於 2012 年開發並於 2015 年開源,專門解決複雜移動應用的資料聚合難題。
1. 客戶端按需宣告(No Over-fetching)
前端透過單一查詢語句宣告精準欄位,伺服器保證回傳完全一致的 JSON 形狀:
query GetOrderDetails($id: ID!) {
order(id: $id) {
id
totalAmount
user {
name
avatarUrl
}
items {
productName
price
}
}
}
2. 致命陷阱:後端 N+1 查詢問題
在 GraphQL 的 Resolver 模型中,若查詢 10 筆訂單及其關聯用戶:
- 執行 1 次 SQL 查詢 10 筆訂單:
SELECT * FROM orders LIMIT 10 - 針對每筆訂單的
user欄位,GraphQL 引擎會觸發 10 次獨立查詢:SELECT * FROM users WHERE id = ? - 原本 1 次 SQL 爆炸為 11 次 SQL 查詢(N+1 Problem)。
解決方案:DataLoader 批次合併快取
Meta 開源了 DataLoader 函式庫,利用 Node.js Event Loop 的 Microtask 階段:
- 收集同一個 Tick 內發起的所有
userId。 - 自動將 10 次單筆查詢合併為單一 SQL:
SELECT * FROM users WHERE id IN (1, 2, ... 10)。
// DataLoader 批次加載器原理
const userLoader = new DataLoader(async (userIds) => {
const users = await db.users.findMany({ where: { id: { in: userIds } } });
return userIds.map((id) => users.find((u) => u.id === id));
});
三大通訊典範深度決策維度
| 評估維度 | gRPC (Protobuf) | REST (JSON) | GraphQL (AST Query) |
|---|---|---|---|
| 通訊協定 | HTTP/2 二進位 | HTTP/1.1 或 HTTP/2 | HTTP/1.1 或 HTTP/2 (POST) |
| 資料格式 | Protocol Buffers | JSON | JSON (按 Query 格式回傳) |
| Schema 約束性 | 強 (編譯期強制生成 SDK) | 弱至中 (需手動維護 OpenAPI) | 強 (GraphQL Schema SDL) |
| 傳輸效率與延遲 | 極致 (最小 Payload & CPU) | 中等 (JSON 字串開銷) | 中等 (需伺服器 AST 解析) |
| HTTP 快取友好度 | 差 | 極佳 (天然 CDN / 瀏覽器) | 差 (全部走 POST 請求) |
| 客戶端靈活性 | 固化 (需發布新 Proto) | 固化 (依賴後端 API 定義) | 極高 (前端自主宣告欄位) |
| 除錯與工具鏈體驗 | 較高 (需專用工具) | 極低 (cURL / 瀏覽器直開) | 良好 (GraphiQL 互動式操場) |
| 主要應用場景 | 內部微服務高速 RPC | 公開 API / 第三方整合 | 複雜前端視圖 / 多端 BFF |
現代架構最佳實踐組合(Hybrid Architecture)
在成熟的企業級系統中,最佳實踐絕非「三選一」,而是**「分層混用、各司其職」**:
- 內部微服務之間(Service-to-Service):全面採用 gRPC。享受強型別編譯保護、低延遲與最小化伺服器資源消耗。
- 對外公開與開放平台(Public APIs):一律採用 REST / OpenAPI。提供全球開發者最熟悉的體驗與最大化 CDN 快取效率。
- 多端前端與複雜儀表板(Client-to-BFF):採用 GraphQL 或輕量 REST BFF。消除移動端 Over-fetching 與網路瀑布請求。
參考資料與一手文獻
- gRPC: Core Concepts and Architecture Documentation
- GraphQL: Official GraphQL Specification & Best Practices
- Martin Fowler: Richardson Maturity Model for REST APIs
- ByteByteGo: System Design 101: gRPC vs REST vs GraphQL
