負載平衡與閘道器
🎯 核心問題
當單台伺服器扛不住時,如何把流量「聰明地」分配到多個伺服器執行個體? 負載平衡是現代分散式系統的「分發員」。本文透過真實案例(飲料店收銀、快遞分揀、交通指揮)深入理解負載平衡的設計哲學和工程實踐。
1. 為什麼要「負載平衡」?
1.1 從一個真實案例說起:某網站的架構演進
某創業公司在使用者量快速增長時遇到了嚴重的效能問題:
情境還原:
階段一:單台伺服器
使用者 → 伺服器(1 核 2G)
↓
日活 1000 → 活躍時間:1000 人同時存取
↓
問題:CPU 100%,回應慢,經常當機⚠️ 單台伺服器的致命問題
- 效能瓶頸:CPU 100%,回應時間 > 5 秒
- 單點故障:伺服器掛了,整個網站無法使用
- 擴充困難:只能垂直升級(加 CPU、記憶體),貴且有限
改進後的架構(引入負載平衡):
階段二:多台伺服器 + 負載平衡
使用者 → 負載平衡器(Nginx)
↓
├→ 伺服器 1(1 核 2G)
├→ 伺服器 2(1 核 2G)
└→ 伺服器 3(1 核 2G)✨ 改進後的效果
- 效能提升:3 台伺服器並行處理,回應時間 < 1 秒
- 高可用性:1 台伺服器掛了,其他伺服器繼續服務
- 水平擴充:需要更多效能?加伺服器就行
1.2 負載平衡的生活化比喻
飲料店收銀台
想像你開了一家網紅飲料店:
- 1 個收銀台:顧客排隊,後面的人等不及,負評
- 3 個收銀台:員工分配顧客到不同收銀台,效率提升 3 倍
負載平衡就是「收銀台分配員」:
- 使用者(顧客) → 請求服務
- 負載平衡器(分配員) → 把請求分配到不同伺服器
- 伺服器(收銀台) → 處理請求
基于传输层信息(IP地址+端口)进行流量分发。不关心应用层内容,只做"快递分拣",因此性能极高。
- 需要极高吞吐量的场景
- TCP/UDP流量分发
- 不需要内容识别的场景
- 微服务间通信
2. 什麼是負載平衡?
2.1 四層負載平衡(L4):只看門牌號碼
工作在傳輸層(TCP/UDP),就像快遞員只看你家的門牌號碼(IP 位址 + 連接埠號),不關心你家是做什麼的。
特點:
- 速度超快:只做簡單的位址轉送,不解析資料封包內容
- 適用情境:資料庫連線、Redis 快取、長連線遊戲伺服器
- 代表產品:LVS(Linux Virtual Server)、AWS NLB、Azure Load Balancer
工作原理
用戶端請求 → L4 負載平衡器 → 後端伺服器
↓
只看 IP + Port
↓
快速轉送(不拆封包內容)2.2 七層負載平衡(L7):檢查包裹內容
工作在應用層(HTTP/HTTPS),就像快遞員不僅看門牌號碼,還會打開包裹檢查內容,根據內容決定怎麼送。
特點:
- 智慧路由:可以根據 URL 路徑、HTTP 標頭、Cookie 等做精細化路由
- 進階功能:SSL 卸載、內容快取、壓縮、安全 WAF
- 適用情境:Web 應用、API 閘道器、微服務架構
- 代表產品:Nginx、HAProxy、AWS ALB、Envoy
工作原理
用戶端請求 → L7 負載平衡器 → 解析 HTTP 內容
↓
檢查 URL、Header、Cookie
↓
智慧路由到特定伺服器2.3 L4 vs L7 對比一覽
| 維度 | 四層負載平衡(L4) | 七層負載平衡(L7) |
|---|---|---|
| 工作層級 | 傳輸層(TCP/UDP) | 應用層(HTTP/HTTPS) |
| 決策依據 | IP 位址 + 連接埠號 | URL、Header、Cookie、Body |
| 處理速度 | 極快(核心態處理) | 較快(使用者態解析) |
| 功能豐富度 | 基礎轉送 | SSL 卸載、快取、壓縮、WAF |
| 典型情境 | 資料庫、遊戲、長連線 | Web 應用、API 閘道器、微服務 |
| 代表產品 | LVS、AWS NLB | Nginx、HAProxy、AWS ALB |
3. 核心問題一:如何避免「壞掉」的伺服器繼續接客?
3.1 健康檢查:別讓「生病」的伺服器拖累系統
想像一下,你的某個收銀台突然壞了,但分配員不知道,還在源源不絕地把顧客分過去。結果隊伍越來越長,顧客怨聲載道。
健康檢查(Health Check)就是防止這種情況發生的「哨兵」。它定期「體檢」每台伺服器,發現「生病」的立即從佇列中移除,等「康復」了再請回來。
3.2 主動健康檢查 vs 被動健康檢查
主動健康檢查(Active Health Check):負載平衡器主動「敲門」問伺服器「你還在嗎?」
- 定期傳送探測請求(如 HTTP /health、TCP ping)
- 回應逾時或傳回錯誤碼則認為不健康
- 優點:檢測結果準確可靠
- 缺點:產生額外的探測流量
被動健康檢查(Passive Health Check):負載平衡器「觀察」真實業務流量的回應情況
- 統計實際請求的回應時間、錯誤率
- 連續多次失敗則認為不健康
- 優點:不產生額外流量
- 缺點:需要足夠的流量樣本才能判定
閾值設定表
| 指標 | 健康閾值 | 不健康閾值 | 說明 |
|---|---|---|---|
| HTTP 狀態碼 | 200-399 | 400+ 或逾時 | 4xx/5xx 都認為失敗 |
| TCP 連線 | 成功建立 | 連線逾時 | 檢查連接埠是否可達 |
| 回應時間 | < 500ms | > 2000ms | 逾時時間通常設為 2-5 秒 |
| 連續失敗次數 | - | 3 次 | 避免單次抖動誤判 |
| 檢查間隔 | - | 5s | 太頻繁會增加負載 |
💡 常見踩坑:閾值設定太「敏感」
某團隊將健康檢查的回應時間閾值設為 100ms,而他們的應用平均回應時間在 80-120ms 之間波動。結果是伺服器頻繁被標記為「不健康」,導致流量在健康和不健康之間反覆橫跳,系統整體可用率反而下降。
正確的做法:閾值應該設定為 P99 回應時間的 2-3 倍,給正常波動留出足夠的緩衝空間。
4. 核心問題二:如何保證「老顧客」一直找同一個「收銀員」?
4.1 工作階段保持:讓「老顧客」一直找同一個「收銀員」
想像你是飲料店的常客,每次來都由同一個店員接待。她知道你的口味偏好(半糖、去冰),服務起來又快又貼心。但如果每次來都換一個新人,你得一遍遍重複同樣的要求,效率大打折扣。
工作階段保持(Session Persistence / Sticky Session) 就是解決這個問題的方法:確保同一個使用者的請求,始終被路由到同一台後端伺服器。
4.2 三種工作階段保持機制對比
| 機制 | 實作原理 | 優點 | 缺點 | 適用情境 |
|---|---|---|---|---|
| Cookie 插入 | LB 在回應中插入 Cookie,後續請求攜帶此 Cookie | 不受 IP 變化影響,首次請求即可保持 | 用戶端需支援 Cookie,可能被停用 | 電商購物車、登入態保持 |
| IP 雜湊 | 對用戶端 IP 做雜湊計算,對應到特定伺服器 | 無需用戶端支援,無狀態 | IP 變化會遺失工作階段,難以均勻分布 | 無 Cookie 環境、WebSocket |
| 黏性工作階段表 | LB 維護工作階段到伺服器的對應表 | 支援工作階段複製和故障轉移 | 佔用 LB 記憶體,需要額外同步 | 高可用性要求嚴格的場景 |
💡 使用建議
- Cookie 插入:優先推薦,相容性好
- IP 雜湊:只用於 WebSocket 等特殊情境
- 黏性工作階段表:配合 Cookie,提供故障轉移能力
5. 核心問題三:如何實現零停機部署?
5.1 藍綠部署:「一鍵切換」的零停機發布
核心思想:同時維護兩套完全相同的生產環境(藍環境和綠環境),但只有一個環境對外提供服務。
- 零停机时间:流量切换在毫秒级完成,用户无感知
- 快速回滚:发现问题可立即切回原环境,风险可控
- 完整的预发布测试:新环境可完整测试后再接管流量
- 数据一致性:无需处理新旧版本同时运行时的兼容问题
- 资源成本高:需要同时维护两套完整环境,服务器成本翻倍
- 数据库兼容性挑战:如果涉及数据库Schema变更,需要特别处理兼容性
- 预热问题:新环境启动后可能需要时间预热缓存、连接池等
- 不适合有状态服务:对于长连接、会话保持要求高的场景处理复杂
工作流程:
- 初始狀態:藍環境執行 v1.0(生產),綠環境待命。
- 部署新版本:在綠環境部署 v1.1,進行內部冒煙測試。
- 切換流量:將負載平衡器指向綠環境,流量瞬間切換到 v1.1。
- 監控觀察:觀察綠環境執行狀態,確認無異常。
- 保留舊版本:藍環境保持 v1.0 一段時間(如 24 小時),作為快速復原的保險。
✨ 優缺點分析
| 優點 | 缺點 |
|---|---|
| ✅ 零停機時間,切換在毫秒級完成 | ❌ 資源成本高,需要同時維護兩套環境 |
| ✅ 快速復原,發現問題立即切回原環境 | ❌ 資料庫 Schema 變更時需要特別處理相容性 |
| ✅ 新環境可完整測試後再接管流量 | ❌ 不適用於有狀態服務(如 WebSocket 長連線) |
5.2 金絲雀發布:「小步快跑」的灰度策略
金絲雀發布得名於歷史上的「煤礦金絲雀」——礦工帶著金絲雀下礦井,如果金絲雀出現異常,說明有毒氣體外洩,礦工立即撤離。在軟體發布中,金絲雀發布就是先讓一小部分使用者試用新版本,觀察沒有問題後再逐步擴大範圍。
- 1% → 5% → 10% → 25% → 50% → 100%
- 每个阶段观察至少15-30分钟
- 关键指标:错误率、延迟、吞吐量
- 内部员工/测试用户先行
- 按地域:选择特定区域用户
- 按用户属性:VIP用户或普通用户
- 按设备类型:iOS/Android/Web
- 错误率超过阈值自动回滚
- P99延迟异常触发告警
- 关键业务指标下降自动回滚
- 一键回滚:30秒内恢复旧版本
- 基础设施:CPU、内存、磁盘、网络
- 应用指标:QPS、错误率、延迟分布
- 业务指标:转化率、订单量、收入
- 用户体验:页面加载时间、交互延迟
核心思想:
- 小流量先行:先將 1% 的流量導入新版本伺服器。
- 觀察指標:持續監控錯誤率、延遲、業務關鍵指標。
- 逐步放量:如果一切正常,逐步將比例提升到 5%、10%、25%、50%、100%。
- 快速復原:一旦發現異常,立即將所有流量切回舊版本。
💡 金絲雀發布的優勢
| 優勢 | 說明 |
|---|---|
| 🎯 風險可控 | 即使新版本有嚴重 Bug,也只影響少量使用者 |
| 📊 真實驗證 | 在真實生產環境驗證,比測試環境更可靠 |
| 🚀 快速迭代 | 團隊可以更自信地頻繁發布新功能 |
| 💰 資源友善 | 不需要像藍綠部署那樣準備兩套完整環境 |
6. 核心問題四:如何讓系統自己「呼吸」?
6.1 自動擴縮容:讓系統像餐廳一樣「靈活排班」
想像你開了一家餐廳:
- 午餐尖峰時段:需要 10 個服務生,但下午 3 點離峰時段只需要 2 個
- 如果一直維持 10 個**:人事成本爆炸
- 如果一直只有 2 個:尖峰時段顧客等不及,全跑了
自動擴縮容(Auto Scaling) 就是讓系統像餐廳一樣「靈活排班」——忙的時候自動加伺服器,閒的時候自動減伺服器。
6.2 擴充指標的選擇
自動擴縮容的核心是回答一個問題:** 什麼時候該加機器?什麼時候該減機器?
常見的決策指標:
| 指標 | 擴充閾值 | 縮減閾值 | 適用情境 |
|---|---|---|---|
| CPU 使用率 | > 70% | < 30% | 計算密集型應用 |
| 記憶體使用率 | > 75% | < 40% | 記憶體密集型應用 |
| QPS(每秒請求數) | > 1000/s | < 400/s | API 閘道器、Web 服務 |
| 連線數 | > 5000 | < 1000 | 資料庫、訊息佇列 |
| 自訂業務指標 | 視業務而定 | 視業務而定 | 特定業務情境 |
💡 擴充策略的「坑」與「解」
坑 1:擴充反應太慢,流量洪峰已經把系統打掛了
某電商大促期間,設定 CPU > 80% 觸發擴充,但監控擷取有 1 分鐘延遲,新執行個體啟動需要 3 分鐘。結果流量來得太快,擴充還沒完成,伺服器已經被打掛。
解決方案:
- 提前擴充:基於歷史資料預測流量尖峰,提前 30 分鐘開始擴充
- 多級閾值:設定 60% 預警(開始預熱新執行個體)、70% 正式擴充、80% 緊急擴充
- 快速擴充:使用容器化部署,新執行個體 30 秒內啟動(相比虛擬機器 3-5 分鐘)
坑 2:擴充太激進,成本爆炸
某創業公司設定了激進的自動擴充策略:CPU > 50% 就擴充。結果一個正常的業務波動就觸發了擴充,伺服器數量從 5 台膨脹到 30 台,月底雲端帳單嚇哭了 CTO。
解決方案:
- 設定擴充冷卻時間:一次擴充後,至少等待 5 分鐘才能再次擴充
- 設定最大執行個體數:max = 目前執行個體數 × 2,防止無限膨脹
- 區分突刺和趨勢:只有連續 3 個週期都超過閾值才擴充,避免單點突刺觸發
坑 3:縮減太快,剛擴充的機器馬上就縮了
某團隊設定了 CPU < 30% 縮減。擴充後流量還在消化,CPU 短暫回落到 25%,觸發了縮減。剛縮完 CPU 又飆到 80%,又觸發擴充——系統在「擴充-縮減-擴充」中瘋狂震盪。
解決方案:
- 縮減更保守:擴充閾值 70%,縮減閾值 25%,中間有足夠的緩衝帶
- 縮減冷卻時間更長:擴充後至少等待 10 分鐘才能縮減
- 漸進式縮減:一次只縮 1 台,觀察後再決定要不要繼續縮
7. 實戰:如何選擇負載平衡器?
7.1 主流負載平衡器對比
| 特性 | Nginx | HAProxy | Envoy | 雲端廠商負載平衡 |
|---|---|---|---|---|
| 定位 | 高效能反向代理/負載平衡 | 開源負載平衡 | 雲端原生代理 | 託管負載平衡 |
| 效能 | 極高(C 語言,事件驅動) | 高(事件驅動) | 高(C++/Rust) | 極高 |
| 功能豐富度 | 基礎負載平衡、靜態檔案、快取 | 豐富的負載平衡演算法 | 進階路由、觀測 | 功能全面 |
| 組態設定 | 設定檔(nginx.conf) | 設定檔(haproxy.cfg) | API/設定檔 | UI 主控台 |
| 擴充 | C 模組/Lua 指令碼 | Lua 指令碼 | WASM/Filter | 外掛程式 |
| 適用情境 | 靜態資源、七層負載平衡、SSL 終結 | 七層負載平衡、高可用性 | 服務網格、多雲端 | 快速上手 |
💡 選型建議
決策樹:
選擇負載平衡器:
│
├─ 只需要基礎的四層負載平衡?
│ ├─ 是 → LVS(開源免費)或 雲端廠商 NLB
│ └─ 否 → 繼續
│
├─ 需要服務網格、多雲端部署?
│ ├─ 是 → Envoy
│ └─ 否 → 繼續
│
├─ 需要極其複雜的組態設定和外掛程式?
│ ├─ 是 → HAProxy
│ └─ 否 → 繼續
│
├─ 需要高效能 + 簡單組態設定?
│ ├─ 是 → Nginx(首選)
│ └─ 繼續
│
├─ 想要託管維運?
│ ├─ 是 → 雲端廠商負載平衡(AWS ALB、阿里雲 SLB)
│ └─ Nginx 自建8. 總結:負載平衡的核心思維
8.1 核心原則回顧
| 原則 | 含義 | 實踐要點 |
|---|---|---|
| 分層 | L4 處理「快遞分揀」(快但簡單) | L4 處理資料庫、遊戲;L7 處理 Web、API |
| 冗餘 | 單點故障是架構的敵人 | 透過多執行個體、多區域部署提升可用性 |
| 漸進 | 發布新版本不要「一刀切」 | 藍綠部署實現零停機;金絲雀實現風險可控 |
| 彈性 | 系統應該像生命體一樣「呼吸」 | 忙時自動擴充,閒時自動縮減 |
8.2 設計檢查清單
在引入負載平衡前,問自己以下問題:
- [ ] 是否真的需要負載平衡?(單機效能是否真的不夠)
- [ ] 選擇 L4 還是 L7?(根據業務情境)
- [ ] 如何處理工作階段保持?(Cookie、IP 雜湊、工作階段表)
- [ ] 如何實現健康檢查?(主動、被動、閾值設定)
- [ ] 如何實現零停機?(藍綠部署、金絲雀)
- [ ] 如何實現彈性?(擴縮指標、冷卻時間、最大執行個體數)
9. 名詞速查表
| 名詞 | 英文 | 解釋 |
|---|---|---|
| 負載平衡器 | Load Balancer | 將流量分發到多個後端伺服器的裝置或軟體 |
| 四層負載平衡 | L4 Load Balancing | 基於傳輸層(TCP/UDP)的負載平衡 |
| 七層負載平衡 | L7 Load Balancing | 基於應用層(HTTP/HTTPS)的負載平衡 |
| 健康檢查 | Health Check | 定期檢查後端伺服器的健康狀態的機制 |
| 工作階段保持 | Session Persistence | 確保同一使用者的請求始終路由到同一台伺服器 |
| 黏性工作階段 | Sticky Session | 另一種稱呼,同 Session Persistence |
| 藍綠部署 | Blue-Green Deployment | 兩套環境切換的零停機發布策略 |
| 金絲雀發布 | Canary Release | 小流量先行驗證的灰度發布策略 |
| 自動擴縮容 | Auto Scaling | 根據負載自動增加或減少伺服器數量 |
| 水平擴充 | Horizontal Scaling | 增加伺服器數量來提升處理能力 |
| 垂直擴充 | Vertical Scaling | 提升單機組態(CPU、記憶體)來提升處理能力 |
| 多區域 | Multi-Region | 在多個地理區域部署服務 |
| 多活 | Active-Active | 多個區域同時對外提供服務 |
| 主備 | Active-Standby | 只有一個區域提供服務,其他待命 |
| 資料同步 | Data Replication | 跨區域的資料複製機制 |
| RTO | Recovery Time Objective (RTO) | 復原時間目標,系統故障後需要在多長時間內復原 |
| RPO | Recovery Point Objective (RPO) | 復原點目標,系統故障後可以接受的資料遺失量 |