【系統設計白話文】#03 應用層協定與 API 設計 — 從 HTTP 到 WebSocket

測驗:應用層協定與 API 設計 — 從 HTTP 到 WebSocket

共 5 題,點選答案後會立即顯示結果

1. HTTP 協定的通訊模式是什麼?

  • A. 雙向持續連線,伺服器可以隨時推送資料
  • B. 一問一答:客戶端發請求,伺服器回回應,然後結束
  • C. 點對點直連,不需要經過伺服器
  • D. 發布-訂閱模式,透過 Broker 轉發訊息

2. 在 MQTT 協定中,負責接收和轉發訊息的角色叫什麼?

  • A. Signaling Server
  • B. STUN Server
  • C. Broker
  • D. Publisher

3. 以下哪個 API 設計符合 RESTful 最佳實踐?

A. GET /getUser?id=123 B. POST /v1/users C. POST /deleteUser D. GET /fetchAllUsersList
  • A. GET /getUser?id=123
  • B. POST /v1/users
  • C. POST /deleteUser
  • D. GET /fetchAllUsersList

4. 你正在設計一個即時聊天 App,訊息需要即時送達,伺服器也要能主動推送訊息給客戶端。最適合用哪種協定來傳遞訊息?

  • A. HTTP,因為它是最基本也最穩定的
  • B. MQTT,因為它支援發布-訂閱模式
  • C. WebSocket,因為它支援雙向持續連線
  • D. WebRTC,因為它延遲最低

5. 當你呼叫 API 收到 HTTP 狀態碼 429 時,代表什麼意思?應該怎麼做?

  • A. 伺服器內部錯誤,應該聯繫後端團隊修復
  • B. 找不到資源,應該檢查 URL 是否正確
  • C. 沒有權限,應該重新登入取得授權
  • D. 請求太頻繁被限流,應該等待一段時間後重試

**系列**:系統設計白話文(第 3 篇,共 5 篇)
**難度**:L2-進階
**前置知識**:【系統設計白話文】#02 設計需求與網路基礎
**影片來源**:freeCodeCamp “[System Design Concepts Course and Interview Prep](https://www.youtube.com/watch?v=F2FmTdLtb_4)” by Hayk Simonyan

一句話說明

應用層協定決定了「前端跟後端怎麼對話」,API 設計決定了「對話的規則」。


你會學到什麼

讀完這篇,你會能夠:

  • 比較 HTTP、WebSocket、WebRTC、MQTT 四種協定,知道它們各自在幹嘛
  • 列出設計好用 API 的關鍵原則(版本控制、分頁、錯誤處理等)
  • 碰到不同應用需求時,判斷該用哪種協定

先搞懂一件事:什麼是「應用層協定」?

上一篇我們講了網路基礎。你已經知道資料在網路上怎麼傳輸。但「傳輸」只是把資料搬過去,應用層協定決定的是——資料用什麼格式、按什麼規則交換

打個比方:

  • 傳輸層(TCP/UDP)=馬路和卡車,負責把東西運過去
  • 應用層協定(HTTP 等)=包裹上的格式標籤,告訴收件人「這是什麼、要怎麼處理」

你每天用的 Web 應用,背後幾乎都在用應用層協定。這篇要介紹四種最常見的。


協定一:HTTP — 你最熟的那個

一句話說明

HTTP 是「一問一答」模式:客戶端發請求,伺服器回回應,然後結束。

最小範例

客戶端 → 伺服器:GET /api/users/123
伺服器 → 客戶端:{ "id": 123, "name": "Alice" }
(對話結束)
Code language: JavaScript (javascript)

白話翻譯

HTTP 就像寄信:你寫一封信(請求)寄出去,對方收到後回你一封信(回應),就這樣。每次要新資料,都得再寄一封新的信。

核心概念翻譯

你會看到 意思
GET /users 「請給我使用者列表」
POST /users 「我要新增一個使用者」
PUT /users/123 「我要整個覆蓋使用者 123 的資料」
PATCH /users/123 「我要修改使用者 123 的某些欄位」
DELETE /users/123 「請刪掉使用者 123」
200 OK 「好的,處理完了,這是結果」
404 Not Found 「找不到你要的東西」
500 Internal Server Error 「伺服器自己出問題了」

適合什麼情境?

  • 大多數的 Web 應用(網頁瀏覽、表單送出、API 呼叫)
  • 資料不需要「即時推送」的場景
  • RESTful API 的基礎

看到 HTTP 請求時要確認

  • [ ] 用了哪個動詞(GET / POST / PUT / DELETE)?
  • [ ] 回應的狀態碼是什麼(2xx 成功、4xx 客戶端錯誤、5xx 伺服器錯誤)?
  • [ ] 有沒有帶 Authorization header(認證資訊)?

協定二:WebSocket — 持續對話的通道

一句話說明

WebSocket 是「開一條電話線,雙方可以隨時說話」,不用每次都重新打電話。

最小範例

// 客戶端建立 WebSocket 連線
const ws = new WebSocket("ws://example.com/chat");

ws.onopen = () => {            // 連線建立成功時
  ws.send("Hello!");           // 傳訊息給伺服器
};

ws.onmessage = (event) => {    // 收到伺服器訊息時
  console.log(event.data);     // 印出來
};
Code language: JavaScript (javascript)

逐行翻譯

const ws = new WebSocket("ws://example.com/chat");
// ↑ 跟 example.com 建立一條持續連線(注意是 ws:// 不是 http://)

ws.onopen = () => {
// ↑ 「連線成功時」要做的事

  ws.send("Hello!");
  // ↑ 透過這條連線傳訊息出去

ws.onmessage = (event) => {
// ↑ 「收到訊息時」要做的事(伺服器可以隨時主動推訊息過來!)

  console.log(event.data);
  // ↑ 把收到的訊息印出來
Code language: JavaScript (javascript)

HTTP vs WebSocket 對比

HTTP(一問一答):
客戶端 → 有新訊息嗎? → 伺服器:沒有
客戶端 → 有新訊息嗎? → 伺服器:沒有
客戶端 → 有新訊息嗎? → 伺服器:有!給你
(每次都要重新問)

WebSocket(持續連線):
客戶端 ←→ 伺服器(連線建立)
伺服器 → 有新訊息了!直接推給你
伺服器 → 又有一則!直接推給你
(連線一直開著,伺服器主動推送)

適合什麼情境?

  • 聊天室(LINE、Discord 的即時訊息)
  • 即時通知(股票報價、運動比分)
  • 協作編輯(Google Docs 多人同時編輯)
  • 線上遊戲(需要即時同步遊戲狀態)

看到 WebSocket 時要確認

  • [ ] 連線有沒有處理斷線重連的邏輯?
  • [ ] 有沒有處理 onerroronclose 事件?
  • [ ] URL 是 ws:// 還是 wss://(加密版本)?

協定三:WebRTC — 不經過伺服器的點對點通訊

一句話說明

WebRTC 讓兩個瀏覽器直接對話,不用繞過伺服器,適合視訊通話。

運作方式白話版

一般通訊(經過伺服器):
Alice → 伺服器 → Bob

WebRTC(點對點):
Alice ←→ Bob(直接連線,不經過伺服器)

但要先「牽線」:
Alice → 信令伺服器(我想跟 Bob 通話)
Bob ← 信令伺服器(Alice 想跟你通話)
Alice ←→ Bob(好,我們直接連)

核心概念翻譯

你會看到 意思
Peer-to-Peer (P2P) 兩個裝置直接通訊,不經過中間人
Signaling Server 「牽線伺服器」,只負責讓雙方知道彼此存在
STUN Server 幫你找到自己的公開 IP(因為你在路由器後面)
TURN Server 當兩方真的無法直連時,退而求其次當中繼站
ICE Candidate 可能的連線路徑,系統會自動選最快的那條

適合什麼情境?

  • 視訊通話(Google Meet、Zoom 的部分功能)
  • 語音通話
  • 螢幕分享
  • P2P 檔案傳輸

看到 WebRTC 時要確認

  • [ ] 有沒有設定 STUN/TURN 伺服器?(沒有的話很多網路環境會連不上)
  • [ ] 是純 P2P 還是有 fallback 到伺服器轉發?
  • [ ] 有沒有處理權限請求(攝影機、麥克風)?

協定四:MQTT — IoT 設備的輕量級通訊

一句話說明

MQTT 是「訂報紙」模式:設備訂閱特定主題,有新資料時自動收到,專為低功耗設備設計。

運作方式白話版

發布-訂閱模式:

溫度感測器 → 發布到「kitchen/temperature」主題 → Broker(中間人)
                                                    ↓
手機 App ← 訂閱「kitchen/temperature」主題 ← Broker
儀表板   ← 訂閱「kitchen/temperature」主題 ← Broker

感測器只管發布,不用知道誰在聽
手機和儀表板只管訂閱,不用知道誰在發
Broker 負責轉發

核心概念翻譯

你會看到 意思
Broker 訊息中間人,負責接收和轉發
Publish 發布訊息到某個主題
Subscribe 訂閱某個主題,有新訊息就收到
Topic 訊息的分類,像資料夾路徑(例如 home/bedroom/light
QoS 0 最多送一次(可能漏掉)
QoS 1 至少送一次(可能重複)
QoS 2 剛好送一次(最可靠但最慢)

適合什麼情境?

  • IoT 設備(智慧家居感測器、工廠設備監控)
  • 低頻寬、不穩定的網路環境
  • 設備功耗很低,不能一直維持複雜連線

MQTT vs HTTP 差在哪?

HTTP(每次都完整請求):
感測器 → POST /api/temperature { "value": 25.3 }
         ↑ 每次要帶完整的 HTTP headers,很重

MQTT(輕量發布):
感測器 → 發布 "25.3""kitchen/temp"
         ↑ 封包很小,省電省頻寬
Code language: JavaScript (javascript)

四種協定怎麼選?決策樹

遇到系統設計問題時,用這個流程判斷:

你的應用需要什麼?
│
├─ 一般的 Web API(讀寫資料)
│   → 用 HTTP(REST API)
│
├─ 即時雙向通訊(聊天、通知、即時更新)
│   → 用 WebSocket
│
├─ 視訊/語音通話、螢幕分享(需要低延遲 P2P)
│   → 用 WebRTC
│
└─ IoT 設備、感測器(低功耗、低頻寬)
    → 用 MQTT

一張表看懂四種協定

特性 HTTP WebSocket WebRTC MQTT
通訊方向 一問一答 雙向 雙向(P2P) 發布-訂閱
連線方式 每次新連線 持續連線 點對點 透過 Broker
延遲 極低
適合場景 Web API 聊天/通知 視訊通話 IoT
複雜度
需要伺服器? 只需信令伺服器 需要 Broker

API 設計最佳實踐:讓你的 API 好用又好維護

知道了協定之後,最常用的還是 HTTP。而基於 HTTP 的 API 怎麼設計才好用?這就是 REST API 設計的核心問題。

好的 API vs 壞的 API

先看對比,你就知道差在哪:

壞的 API(看不出在幹嘛):
GET  /getUser?id=123
POST /createNewUser
POST /deleteUser
GET  /fetchAllUsersList

好的 API(一看就懂):
GET    /v1/users/123       ← 取得使用者 123
POST   /v1/users           ← 新增使用者
DELETE /v1/users/123       ← 刪除使用者 123
GET    /v1/users           ← 取得使用者列表

原則一:用名詞不用動詞

壞:GET /getUsers          ← 動詞 + 名詞,冗餘
好:GET /users             ← HTTP 動詞已經表示動作了

翻譯:HTTP 方法(GET、POST、DELETE)本身就是動詞了,URL 只放名詞就好。

原則二:版本控制

/v1/users     ← 第一版 API
/v2/users     ← 第二版,可能改了回傳格式

翻譯:API 會改版。加版號讓舊的 App 不會因為你更新 API 而壞掉。

原則三:分頁(Pagination)

壞:GET /users             ← 一次回傳全部 10 萬筆?伺服器炸了
好:GET /users?page=1&limit=20  ← 每次只回 20 筆,第 1 頁

翻譯:資料很多時,分批給。跟 Google 搜尋結果分頁一樣的道理。

常見的分頁參數

參數 意思
page=2&limit=20 第 2 頁,每頁 20 筆
offset=40&limit=20 跳過前 40 筆,取 20 筆
cursor=abc123 從上次結果的標記繼續往下取(效能最好)

原則四:正確使用狀態碼

壞:永遠回 200,把錯誤藏在 body 裡
{
  "status": 200,
  "error": "User not found"   ← 明明找不到,還回 200?
}

好:用正確的 HTTP 狀態碼
404 Not Found
{
  "error": "User not found",
  "code": "USER_NOT_FOUND"
}
Code language: JavaScript (javascript)

必看懂的狀態碼分類

範圍 意思 常見
2xx 成功 200 OK、201 Created
3xx 重新導向 301 永久搬家、302 暫時搬家
4xx 你(客戶端)的問題 400 Bad Request、401 沒登入、403 沒權限、404 找不到、429 Too Many Requests
5xx 我(伺服器)的問題 500 Internal Error、503 Service Unavailable

原則五:限流(Rate Limiting)

當你呼叫 API 太頻繁:

HTTP/1.1 429 Too Many Requests
{
  "error": "Rate limit exceeded",
  "retry_after": 6060 秒後再試
}
Code language: JavaScript (javascript)

翻譯:API 有使用額度,超過就會被暫時擋住。保護伺服器不被打爆。

常見的限流回應 Header

X-RateLimit-Limit: 100        ← 每小時最多 100 次
X-RateLimit-Remaining: 23     ← 還剩 23 次
X-RateLimit-Reset: 1672531200 ← 額度重置的時間(Unix timestamp)
Code language: HTTP (http)

原則六:一致的錯誤回應格式

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Email format is invalid",
    "details": [
      {
        "field": "email",
        "issue": "Must be a valid email address"
      }
    ]
  }
}
Code language: JSON / JSON with Comments (json)

翻譯:每個錯誤回應都長得一樣,前端才好統一處理。


實戰範例:從需求判斷該用什麼

範例 1:電商網站

需求:瀏覽商品、下單、查訂單
分析:
  - 一般的讀寫操作 → HTTP REST API
  - 訂單狀態更新通知 → 可以用 HTTP 輪詢,或升級用 WebSocket

選擇:以 HTTP 為主,通知部分視規模決定

範例 2:即時聊天 App

需求:一對一聊天、群組聊天、已讀通知
分析:
  - 訊息要即時送達 → 需要持續連線
  - 伺服器要能主動推訊息 → HTTP 做不到

選擇:WebSocket(訊息傳遞)+ HTTP(使用者管理、歷史訊息查詢)

範例 3:視訊會議

需求:多人視訊通話、螢幕分享
分析:
  - 影音串流需要極低延遲 → WebSocket 延遲還是太高
  - 最好不經過伺服器 → 點對點直連

選擇:WebRTC(影音串流)+ WebSocket(信令 + 聊天)+ HTTP(使用者管理)

範例 4:智慧工廠監控

需求:1000 個感測器回報溫度、濕度
分析:
  - 設備功耗低 → 需要輕量級協定
  - 資料是「發布」性質 → 不需要一問一答
  - 多個面板需要訂閱同一批資料

選擇:MQTT(感測器資料收集)+ HTTP(管理介面)+ WebSocket(即時儀表板)

Vibe Coder 檢查點

看到系統架構或 API 設計時,確認這些:

協定選擇

  • [ ] 這個功能需要即時雙向通訊嗎?(是 → 考慮 WebSocket)
  • [ ] 需要視訊/語音嗎?(是 → 考慮 WebRTC)
  • [ ] 是 IoT 設備嗎?(是 → 考慮 MQTT)
  • [ ] 以上都不是?(→ HTTP 就夠了)

API 設計

  • [ ] URL 有版本號嗎?(/v1/users
  • [ ] 有分頁嗎?(列表 API 一定要有)
  • [ ] 狀態碼正確嗎?(不是都回 200)
  • [ ] 有限流機制嗎?(公開 API 一定要有)
  • [ ] 錯誤回應格式一致嗎?

必看懂 vs 知道就好

必看懂(會一直出現):
- HTTP 方法(GET、POST、PUT、DELETE)
- 狀態碼的分類(2xx、4xx、5xx)
- WebSocket 的 ws:// 連線和事件處理
- REST API 的 URL 命名規則
- 分頁和限流的基本概念

知道就好(遇到再查):
- gRPC:Google 的高效能 RPC 框架,用 Protocol Buffers 序列化
- GraphQL:Facebook 的查詢語言,客戶端指定要哪些欄位
- Server-Sent Events (SSE):伺服器單向推送,比 WebSocket 簡單
- HTTP/2 Server Push:伺服器主動推送資源
- AMQP:進階訊息佇列協定,比 MQTT 更複雜但功能更完整
Code language: JavaScript (javascript)

本篇重點回顧

  1. HTTP:一問一答,最基本也最常用。REST API 就是建在 HTTP 上。
  2. WebSocket:持續連線、雙向通訊。適合聊天室、即時通知。
  3. WebRTC:點對點直連,不經過伺服器。適合視訊通話。
  4. MQTT:輕量級發布-訂閱。適合 IoT 設備。
  5. API 設計:好的 API 用名詞 URL、有版本號、有分頁、用正確狀態碼、有限流。

**下一篇預告**:我們會進入資料庫的世界,看看 SQL vs NoSQL 怎麼選、什麼時候該加快取。

進階測驗:應用層協定與 API 設計

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在設計一個智慧農場系統,需要收集 500 個土壤濕度感測器的數據,並在儀表板上即時顯示。感測器電池供電、頻寬有限。以下哪種架構最合適? 情境題

  • A. 感測器用 HTTP POST 定時回報,儀表板用 HTTP 輪詢更新
  • B. 感測器和儀表板都用 WebSocket 連到伺服器
  • C. 感測器用 MQTT 發布數據到 Broker,儀表板用 WebSocket 即時顯示
  • D. 感測器用 WebRTC 直接點對點連到儀表板

2. 你負責一個公開 API,使用者反映「列表 API 回應很慢,有時候甚至 timeout」。調查後發現該 API 一次回傳所有資料(目前有 8 萬筆)。以下哪個改善方案最優先? 情境題

GET /v1/products ← 回傳全部 80,000 筆商品
  • A. 把 HTTP 改成 WebSocket,用串流方式分批送出資料
  • B. 加入分頁機制,例如 GET /v1/products?page=1&limit=20
  • C. 把 API 版本升級到 v2,使用 gRPC 提高傳輸效率
  • D. 增加限流機制,限制每個使用者每小時只能呼叫 10 次

3. 你的團隊正在開發線上教學平台,其中包含:學生管理、課程瀏覽、即時問答聊天室、一對一視訊家教。以下哪種協定組合最合適? 情境題

  • A. 全部使用 HTTP REST API 就夠了
  • B. 全部使用 WebSocket,統一管理比較方便
  • C. HTTP(學生管理、課程瀏覽)+ WebSocket(聊天室 + 視訊家教)
  • D. HTTP(學生管理、課程瀏覽)+ WebSocket(聊天室)+ WebRTC(視訊家教)

4. 同事設計了以下 API,你在 code review 時發現了問題。以下哪個是最關鍵的設計錯誤? 錯誤診斷

POST /v1/getAllUsers ← 取得使用者列表 POST /v1/deleteUserById/123 ← 刪除使用者 POST /v1/createNewUser ← 新增使用者 POST /v1/updateUserInfo/123 ← 更新使用者
  • A. 沒有使用 HTTPS 加密連線
  • B. 所有操作都用 POST,沒有正確使用 HTTP 方法(GET、DELETE、PUT),且 URL 包含動詞
  • C. 缺少認證機制,沒有帶 Authorization header
  • D. 沒有加入限流(Rate Limiting)機制

5. 你的 API 在找不到使用者時回傳以下內容,前端同事抱怨「很難判斷請求有沒有成功」。這個回應最大的問題是什麼? 錯誤診斷

HTTP/1.1 200 OK Content-Type: application/json { “status”: 200, “data”: null, “error”: “User not found” }
  • A. 回應缺少 X-RateLimit 相關的 header
  • B. 錯誤訊息應該用錯誤代碼(如 USER_NOT_FOUND)而非文字描述
  • C. 找不到資源卻回傳 HTTP 200,應該使用 404 狀態碼,讓前端能透過狀態碼判斷成功或失敗
  • D. 沒有使用版本控制,URL 應該加上 /v1/ 前綴

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *