主題
0.6 · 網路地基
Part 0 前置知識
預估 22 分鐘
難度:入門
TCP
HTTP
RPC
TL;DR · 本章重點
- TCP 提供「可靠、有序、有流量控制」的傳輸:但「可靠」只是「最終會送到或斷線」、不是即時。timeout 與重傳會引入不可預測的延遲。
- HTTP 建在 TCP 上:無狀態、request-response。REST API 是它的應用層慣例。
- RPC 假裝成本地呼叫、但本質仍是網路:永遠會遇到 timeout、變動延遲、partial failure —— DDIA Ch4 與 Ch8 反覆強調的「漏抽象」。
- 延遲 vs 頻寬是不同維度:光速是延遲的物理下界(跨美國 RTT ~70ms 是物理限制),頻寬可以加錢買、延遲不行。
- Partial failure(部分失效 — 有的節點正常、有的異常,且你不知道是哪些)是分散式系統最難的事:不像單機「整個壞」、網路斷一半時你不知道對方還在不在、自己的訊息有沒有送到。
1) TCP/IP 的分層心智模型
應用層 HTTP, gRPC, Postgres wire protocol, SMTP, ...
↕
傳輸層 TCP(可靠、有序)或 UDP(不保證)
↕
網路層 IP(封包路由)
↕
鏈結層 Ethernet, WiFi, ...1
2
3
4
5
6
7
2
3
4
5
6
7
對 DDIA 來說,傳輸層的 TCP 與應用層的 HTTP / RPC 才是焦點。
2) TCP:可靠不等於即時
三次握手
Client Server
│ SYN │
│ ────────────────────▶ │
│ SYN-ACK │
│ ◀──────────────────── │
│ ACK │
│ ────────────────────▶ │
│ │
│ 建立連線 │1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
只是建立連線就要 1 個 RTT—— 跨美國 RTT ~70ms,意味建立 TCP 連線最少 70ms 後才能傳資料。這是 HTTP/2 為什麼要重用連線、HTTP/3 (QUIC) 為什麼換 UDP 的原因。
超時重傳:「可靠」的代價
TCP 保證資料最終到達——做法是:沒收到 ACK 就重傳。重傳間隔指數退避(exponential backoff,每次倍增間隔避免重傳風暴):100ms → 200ms → 400ms → ...
問題:應用層看到的「延遲」可能是 1ms、也可能是 30 秒(重傳了好幾次)—— 高度不可預測。
DDIA Ch8 的核心訊息
TCP 的可靠保證不能擋住 partial failure 的麻煩——你不知道對方有沒有收到、你的訊息有沒有送出去。設定 timeout 是必要的,但 timeout 多長都會錯:太短誤判活的節點為死、太長拖累整體效能。
TCP Keepalive 預設 7200 秒太長(SRE 必調)
Linux 預設 tcp_keepalive_time = 7200(2 小時)才送第一個 keepalive probe——這對任何「需要在 1 分鐘內知道對方掛了」的應用都太長。
典型問題場景:
- 你的應用持有 DB 連線、DB 端網路設備(防火牆 / LB)默默把 TCP session 從 NAT table 移除
- 應用以為連線還活、繼續送 query、實際送進黑洞
- 你的連線池逐漸耗盡(每條連線都「看起來活、實際死」)
- 90 分鐘後業務開始「莫名其妙超時」、但 monitoring 看不出原因
調整三件套(Linux):
bash
# /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 60 # 60s 沒流量就送 keepalive probe
net.ipv4.tcp_keepalive_intvl = 10 # probe 間隔 10s
net.ipv4.tcp_keepalive_probes = 6 # 6 次失敗才認定連線死
# 總計:60s 閒置 + 60s probe = 2 分鐘內偵測到失連1
2
3
4
5
2
3
4
5
應用層補強:
- 連線池配 healthCheck(HikariCP、pgbouncer):每 30s 拿一條連線跑
SELECT 1、失敗就丟掉 - 設 connection_timeout / socket_timeout 在 client 端、不要永遠等
- 連線池大小 = 預期峰值 QPS × P99 latency(典型 30-100、不要設 1000+)
對應 DDIA Ch8:這正是 §8.2「不可靠網路」+ §8.4「process pause」在連線池層的具體展現——你以為連線活、其實 OS 那層早就斷了。
3) HTTP:無狀態的 request-response
http
GET /api/users/1 HTTP/1.1
Host: example.com
Accept: application/json
→
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 1, "name": "Alice"}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
「無狀態」的真實含義
伺服器不在記憶體裡保留「上一個請求做了什麼」—— 所有上下文要嘛在 cookie、要嘛在 DB。這是為什麼後端應用通常 stateless(0.1 講過)。
REST:HTTP 的應用層慣例
GET /resources取列表GET /resources/:id取單筆POST /resources建立PUT /resources/:id替換DELETE /resources/:id刪除
REST 不是協定,是慣例。DDIA Ch4 對比 REST 與 RPC 兩種風格。
4) RPC:偽裝成函式呼叫的網路請求
python
# 看起來像函式呼叫
result = remote_service.get_user(id=1)1
2
2
底層其實是:
- 序列化參數(Protobuf / Avro / JSON)
- 發 TCP / HTTP 請求
- 等回應
- 反序列化結果
DDIA Ch4 的吐槽
RPC 想偽裝成本地函式呼叫,但永遠做不到。本地函式:
- 不會 timeout
- 延遲 ns 級
- 不會「呼叫成功但結果丟失」
網路:
- 隨時 timeout
- 延遲 ms 級且不可預測
- 「對方收到了但我沒收到 ACK」是真實情境—— 重試會造成重複執行(→ 需要 idempotent)
主流 RPC:gRPC(HTTP/2 + Protobuf)、Thrift、Avro RPC。
5) 延遲 vs 頻寬
完全不同維度:
- 延遲(latency):一個 bit 從 A 到 B 要多久。物理下界是光速。
- 跨美國(5000 km)真空中光速最快 ~17ms 單程、~34ms RTT
- 但訊號在光纖中速度約是真空中的 2/3——所以光纖實際物理下界 ~25ms 單程、~50ms RTT
- 加上路由跳數、序列化延遲,實測 ~70–90ms RTT
- 這是物理常數,加錢不能買低於這個的數字。
- 頻寬(bandwidth):單位時間能送多少 bit。可以加錢買、可以平行傳。
比喻:高速公路
- 延遲 = 從台北開到高雄的最短時間
- 頻寬 = 高速公路有幾條車道1
2
3
2
3
設計分散式系統時:
- 跨地域服務的「最少 100ms 延遲」是物理限制——你的 SLA 必須容納它
- 大批次資料傳輸瓶頸通常是頻寬,可以並行壓縮加速
- 「即時通訊」永遠不能即時—— 只能接近物理下界
6) Partial Failure:分散式系統最難的事
單機程式:要嘛跑、要嘛不跑——清楚。
分散式:A 想跟 B 通訊,但網路斷一半——
- A 不知道訊息有沒有送出去
- A 不知道 B 是不是還活著
- 即使收到 B 的回應,A 不知道是「B 處理完了」還是「B 處理一半就掛了」
一個經典思考題
你發 HTTP POST /api/transfer,等了 30 秒沒回應—— 你該重試嗎?
- 重試:可能造成「轉帳兩次」——除非 server 支援 idempotency key
- 不重試:可能原請求其實沒送到、轉帳沒發生
沒有正確答案——這就是分散式系統的本質難題。DDIA Ch8 全章在拆這個。
7) 與 DDIA 章節的對應
| DDIA 章節 | 用到的網路概念 |
|---|---|
| Ch4 編碼與演進 | RPC vs 訊息傳遞、HTTP API、Protobuf 編碼 |
| Ch5 複製 | 同步 vs 非同步複製(取決於網路 RTT) |
| Ch8 麻煩 | TCP timeout、partial failure、unreliable network |
| Ch9 共識 | 訊息傳遞語意、訊息順序、訊息可能丟失 |
想更深入?
| 資源 | 內容 |
|---|---|
| Beej's Guide to Network Programming | 從 socket API 學起,免費 |
| High Performance Browser Networking | Ilya Grigorik 經典,免費線上版 |
| Cloudflare Learning: How DNS works | 命名系統入門 |
| The Fallacies of Distributed Computing | Peter Deutsch 1994 列的 8 個謬誤,DDIA Ch8 直接引用 |
章末自評
章末測驗 · p0-net
Q1. 應用 你的服務 A 向服務 B 發 RPC,等了 5 秒沒收到回應。下列哪個推論最準確?
Q2. 應用 你發現跨美國服務間呼叫 P99 延遲 130ms,老闆問能不能降到 10ms。最準確的回答是?
Q3. 應用 DDIA Ch4 強調 RPC 不能等同本地函式呼叫,最根本的原因是?
The Next Chapter
0.7 並行控制直覺
Continue Reading→