EP.02系統設計

負載均衡:
讓流量自己找到出路

一台伺服器撐不住?加一台就好。但加了之後,誰決定每個請求去哪? 這篇深入負載均衡的演算法、健康檢查、L4 vs L7 差異, 以及在系統設計面試中如何正確使用它。

Joseph Chen 2026 12 min read Load Balancer · Round Robin · Scalability · High Availability

「負載均衡不只是把流量分散到多台伺服器。 真正的挑戰在於:如何在機器故障時自動切換、 如何讓有狀態的請求(Session)還是落在同一台、 如何在不停機的情況下滾動升級。」

— 分散式系統工程師必修課

為什麼需要負載均衡?

單台伺服器有三個根本限制:CPU 上限記憶體上限網路頻寬上限。 垂直擴展(Scale Up,換更大的機器)可以緩解,但有天花板,且貴。 水平擴展(Scale Out,加更多機器)是解法,但前提是需要有人分配流量——這就是負載均衡器的工作。

提升吞吐量

10 台各能處理 1K QPS 的伺服器,加上 LB 後理論可達 10K QPS。水平擴展的基礎。

高可用性

一台機器掛掉,LB 自動把流量切到其他健康的機器。對用戶透明,感知不到故障。

零停機部署

滾動升級時,LB 先把流量從目標機器移走,升級完再加回來,用戶不中斷。

有 / 無 負載均衡的架構對比

❌ 無 LB(單點)

100K Users
→ 全部流量
Server A
過載 / 單點故障

✅ 有 LB(水平擴展)

100K Users
Load Balancer
Server A
33K
Server B
33K
Server C
34K

負載均衡演算法

LB 怎麼決定把請求送到哪台?不同演算法有不同的適用場景:

Round Robin(輪詢)

最常見

依序輪流分配,A→B→C→A→B→C...。假設所有伺服器性能相同。

Req 1Server A
Req 2Server B
Req 3Server C
Req 4Server A

✅ 適合

服務無狀態、所有機器規格一致、請求處理時間相近

⚠️ 弱點

忽略機器當前負載,可能把請求分到已過載的機器

Weighted Round Robin(加權輪詢)

進階版

高規格機器分到更多請求。A(weight=3):B(weight=1) → A 分到 75% 流量。

✅ 適合

機器規格不同(混合機型)、不同機器承載能力差異大

⚠️ 弱點

靜態設定,不能即時反映機器運行狀態

Least Connections(最少連線)

動態感知

把新請求分給當前「活躍連線數最少」的機器。動態感知負載,更智慧。

✅ 適合

請求處理時間差異大(例如 API 有快有慢)、長連線場景

⚠️ 弱點

需要追蹤每台機器的活躍連線數,LB 本身有額外開銷

IP Hash(IP 雜湊)

Session 黏性

根據客戶端 IP 的 hash 值決定送哪台伺服器。同一個 IP 永遠到同一台機器。

✅ 適合

有狀態的服務(Session 存在機器本地、不共享)、需要保持 Sticky Session

⚠️ 弱點

機器增減時 hash 對應改變,造成 Session 遺失;IP NAT 後可能負載不均

從哪個演算法開始?

預設選 Round Robin:Nginx 和 AWS ALB 的預設值,無狀態服務 + 同規格機器的首選,夠用就不要過度設計。

請求處理時間差異大(有快有慢)→ 改用 Least Connections:避免慢請求堆積到同一台機器。

機器規格不同 → 用 Weighted Round Robin:依硬體能力分配比例流量,通常設定在 Nginx upstream 的 weight 參數。

不要用 IP Hash 解決 Session 問題:機器增減時 hash 對應改變,Session 仍會遺失,這是治標不治本的方案。

L4 vs L7 負載均衡

「L4」和「L7」指的是 OSI 模型的第 4 層(傳輸層)和第 7 層(應用層)。 兩者的核心差異是:LB 看得到多少請求內容,決定路由的資訊豐富程度。

比較項目L4(傳輸層)L7(應用層)
路由依據IP + PortHTTP Header / URL / Cookie / Body
看得到的資訊TCP/UDP 封包HTTP Method、Path、Host、User-Agent
TLS 解密不解密,直接轉發在 LB 終止 TLS,再轉給後端(TLS Termination)
效能極高(不解析封包內容)稍低(需解析 HTTP 請求)
智慧路由無法做內容路由可按 /api/* → API 服務、/static/* → CDN
代表產品AWS NLB、HAProxy (TCP)Nginx、AWS ALB、Traefik、Envoy
使用場景TCP 長連線、遊戲伺服器、DB ProxyWeb API、微服務、A/B Testing

L4 + L7 通常是疊起來用的

這不是二選一的問題。生產環境常見的組合是:DNS 層(Route 53)→ L4 LB(AWS NLB,處理 TCP 連線)→ L7 LB(Nginx / ALB,做 HTTP 路由與 TLS Termination)→ Application Servers。 L4 在外層承受大量 TCP 連線的建立與分發,L7 在內層做更智慧的 HTTP 內容路由。 面試時說「L7 ALB 前面還有一層 L4 NLB 做 DDoS 防護」能讓回答更完整。

L7 路由能力示例:Nginx 設定

nginx.conf
upstream api_servers {
    least_conn;  # 使用 Least Connections 演算法
    server api-1.internal:3000 weight=2;
    server api-2.internal:3000 weight=2;
    server api-3.internal:3000 weight=1;  # 規格較低的機器
}

upstream static_servers {
    server cdn-1.internal:8080;
    server cdn-2.internal:8080;
}

server {
    listen 443 ssl;

    # 根據 URL 路徑路由到不同後端
    location /api/ {
        proxy_pass http://api_servers;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /static/ {
        proxy_pass http://static_servers;
        expires 30d;  # 靜態資源加 Cache-Control
    }

    # 健康檢查端點不進行路由
    location /health {
        return 200 'OK';
    }
}

健康檢查(Health Check)

負載均衡器必須知道哪些後端機器是健康的,才能正確路由。 健康檢查分兩種:

被動健康檢查(Passive)

LB 觀察正常請求的回應。連續 N 次失敗(超時 / 5xx)就標記為不健康。

✅ 不需要額外流量

✅ 反映真實用戶請求的健康狀況

⚠️ 已有真實請求失敗才會偵測到

主動健康檢查(Active)

LB 定時發送探測請求(例如 GET /health 每 5 秒),在真實流量受影響前就偵測到故障。

✅ 提前偵測,不影響真實用戶

✅ 可檢查 DB 連線、依賴服務等深層狀態

⚠️ 需要後端實作 /health 端點

app/api/health/route.ts(Next.js 健康檢查端點)
import { db } from '@/lib/db';

export async function GET() {
  try {
    // 淺層健康:服務本身在線
    const status: Record<string, string> = {
      status: 'ok',
      timestamp: new Date().toISOString(),
    };

    // 深層健康:檢查 DB 連線
    await db.query('SELECT 1');
    status.database = 'ok';

    // 可選:檢查 Redis、外部 API 等依賴
    // await redis.ping();
    // status.cache = 'ok';

    return Response.json(status, { status: 200 });
  } catch (error) {
    return Response.json(
      { status: 'error', message: String(error) },
      { status: 503 }  // Service Unavailable,讓 LB 知道要下線這台
    );
  }
}

建議的健康檢查參數

5 秒

探測間隔

太頻繁會增加後端負載

連續 2 次

失敗閾值

才下線,避免偶發超時誤判

< 100ms

回應期限

/health 本身要夠快

健康檢查的常見陷阱

只檢查 HTTP 200:服務可能在線但 DB 斷線,用戶請求還是全部失敗。

健康檢查頻率太低:30 秒一次,故障後等 30 秒才切換,期間所有請求都失敗。

健康檢查本身造成負載:10 台機器、1 秒一次的深層檢查,等於額外 10 QPS 的 DB 查詢。

Sticky Session 問題與解法

如果應用有狀態(Session 存在機器記憶體),負載均衡後同一個用戶的請求可能落在不同機器上, 導致「明明登入了,下一個請求卻要重新登入」。

方案一:IP Hash(Sticky Session)

同 IP 永遠到同台機器,狀態不丟失。但機器故障 Session 仍消失,且 IP NAT 後負載不均。

⚠️ 治標不治本

方案二:集中式 Session Store

把 Session 存到 Redis(而非本地記憶體)。任何機器都能讀取 Session,LB 可自由分配請求。

✅ 推薦做法

方案三:JWT(無狀態 Token)

用 JWT 取代 Session,Token 本身包含狀態。LB 無需關心,服務也無需共享 Session Store。

✅ 現代 API 首選

⚠️ JWT(方案三)的隱藏限制:無法即時撤銷

JWT 讓 LB 不需要關心 Session,看起來很美好——但代價是: 使用者登出時,你只能清除前端的 Token,伺服器無法讓已簽發的 JWT 立即失效。 如果 Access Token 存活期設 1 小時,被盜的 Token 在 1 小時內仍然有效。 解法:Access Token 設短存活期(15 分鐘)+ Refresh Token 搭配 Redis 黑名單, 登出時把 Refresh Token 加進黑名單,讓惡意方無法換取新 Token。

集中式 Session:Redis 配置(Next.js + iron-session)
// lib/session.ts
import { IronSessionOptions } from 'iron-session';

export const sessionOptions: IronSessionOptions = {
  password: process.env.SECRET_COOKIE_PASSWORD as string,
  cookieName: 'myapp-session',
  // Session 資料存在加密的 Cookie 中(等同 JWT 做法)
  // 若需要 server-side invalidation,改用 Redis:
  // cookieOptions: { maxAge: undefined }
  // 並在 Redis 存 session data,Cookie 只存 session ID
};

// 或使用 next-auth + Redis adapter
// import { RedisAdapter } from '@next-auth/redis-adapter';
// import { createClient } from 'redis';
// const client = createClient({ url: process.env.REDIS_URL });

生產環境的多層負載均衡

大型系統通常不只一層 LB,而是多層組合:

Internet → DNS(Geo-based routing → 就近 Region)
Global LB(AWS Route 53 / Cloudflare)— DNS 層分流
Edge LB / CDN(Cloudflare / AWS CloudFront)— 靜態資源快取、DDoS 防護
L7 LB(AWS ALB / Nginx)— HTTP 路由、TLS Termination、WAF
Application Servers(API 服務、Microservices)
Internal L4 LB(服務間通訊 / DB 前的 Proxy)
Database Cluster(Primary / Replica)

面試時的說法

「在 DNS 層做 Geo-routing 把流量分到最近的 Region, 每個 Region 內用 L7 LB(AWS ALB)把流量分到多台 API 服務, 健康檢查設 5 秒一次,連續 3 次失敗則下線。 Session 用 Redis Cluster 集中存放,讓 LB 可以自由分配請求而不需要 Sticky Session。」

重點整理

Round Robin 是基礎

無狀態服務的預設選擇。加權版(Weighted)應對機器規格不均;Least Connections 應對請求耗時不均。

L7 才能做智慧路由

L4 只看 IP:Port,L7 能看 HTTP Header、URL、Cookie,可做 A/B Testing、藍綠部署、微服務路由。

主動健康檢查是關鍵

每 5 秒探測,連續 2 次失敗就下線。/health 端點要檢查真實依賴(DB、Redis),不能只回 200。

Session 問題用 Redis 解

不要用 Sticky Session(IP Hash)打補丁,把 Session 搬到 Redis,讓服務真正無狀態才是正解。