彈性設計的核心目標是讓系統在故障發生時仍能維持運作,並具備自我恢復的能力。
彈性 (Resiliency) 的含義:系統在不健康、不順甚至出錯的情況下,仍有能力 hold 住、挺住,甚至力挽狂瀾。
隔離設計 (Bulkheads)#
靈感來自船艙的水密隔板設計:當船艙某處進水時,隔板能防止水蔓延到其他艙室。
按服務種類隔離#
flowchart TD
LB[負載均衡器] --> U[用戶服務]
LB --> P[商品服務]
LB --> O[訂單服務]
U --> UDB[(用戶 DB)]
P --> PDB[(商品 DB)]
O --> ODB[(訂單 DB)]
style U fill:#e3f2fd
style P fill:#fff9c4
style O fill:#f3e5f5優點:一個版塊的故障不會影響其他版塊
挑戰:
- 跨版塊資料獲取需要多次呼叫
- 跨版塊事務處理變得複雜
- 需要 Pub/Sub 機制打通資料交換
按用戶隔離(多租戶)#
flowchart LR
subgraph VIP[VIP 用戶]
V1[專用實體]
V1 --> VDB[(專用 DB)]
end
subgraph Normal[普通用戶]
A[用戶 A] --> S[共享實體]
B[用戶 B] --> S
S --> SDB[(共享 DB)]
end
style VIP fill:#fff9c4
style Normal fill:#e8f5e9三種多租戶模式:
| 模式 | 隔離度 | 成本 | 複雜度 |
|---|---|---|---|
| 完全獨立 | 高 | 高 | 低 |
| 共享服務,獨立資料 | 中 | 中 | 中 |
| 完全共享 | 低 | 低 | 高 |
熔斷設計 (Circuit Breaker)#
借鑒電路保險絲的概念:當電流過大時自動斷開,保護電路不被燒毀。
三種狀態#
stateDiagram-v2
[*] --> Closed
Closed --> Open: 失敗次數超過閾值
Open --> HalfOpen: 逾時時間到達
HalfOpen --> Closed: 探測請求成功
HalfOpen --> Open: 探測請求失敗
Closed: 正常狀態<br/>請求正常通過
Open: 熔斷狀態<br/>請求直接失敗
HalfOpen: 半開狀態<br/>允許部分請求探測狀態說明#
| 狀態 | 說明 |
|---|---|
| Closed(關閉) | 正常狀態,請求正常通過,統計失敗次數 |
| Open(打開) | 熔斷狀態,請求直接回傳錯誤或快取資料 |
| Half-Open(半開) | 允許部分請求通過,探測服務是否恢復 |
熔斷設計要點#
- 區分錯誤類型:有些錯誤需要重試後再熔斷,有些應直接熔斷
- 記錄日誌便於監控和診斷
- 提供手動重置功能
- 考慮資源分區:不同分區應有獨立的熔斷器
熔斷器實現邏輯(參考 Netflix Hystrix)
// 1. 判斷是否允許請求
if (!allowRequest()) {
return fallback();
}
// 2. 執行實際呼叫
try {
result = doActualCall();
markSuccess(duration);
return result;
} catch (Exception e) {
markFailure(duration);
return fallback();
}
// 3. 判斷是否需要熔斷
boolean isOpen() {
// 計算錯誤率: failure / (success + failure)
if (errorRate > threshold) {
return true;
}
return false;
}限流設計 (Throttle)#
保護系統不會因過載而崩潰。
限流策略#
| 策略 | 說明 |
|---|---|
| 拒絕服務 | 超過閾值直接拒絕,優先拒絕例外高並行的用戶端 |
| 服務降級 | 關閉非核心功能,或回傳簡化版資料 |
| 特權請求 | VIP 用戶優先處理 |
| 延時處理 | 使用佇列緩衝請求 |
| 彈性伸縮 | 自動擴容後端服務 |
限流演演算法#
計數器#
最簡單的方式,維護一個 Counter:
if (counter > threshold) {
reject();
}漏斗演演算法 (Leaky Bucket)#
請求進入漏斗,以固定速率處理:
flowchart TD
A[請求進入] --> B{佇列是否已滿?}
B -->|否| C[加入佇列]
B -->|是| D[拒絕請求]
C --> E[固定速率處理]
style D fill:#ffcdd2
style E fill:#c8e6c9權杖桶演演算法 (Token Bucket)#
按固定速率產生權杖,請求需要獲取權杖才能執行:
flowchart TD
subgraph Bucket[權杖桶]
T[○ ○ ○ ○ ○ ○ ○]
end
R[固定速率補充] --> Bucket
A[請求到達] --> B{有可用權杖?}
B -->|是| C[獲取權杖]
C --> D[處理請求]
B -->|否| E[等待或拒絕]
style Bucket fill:#fff9c4
style D fill:#c8e6c9
style E fill:#ffcdd2權杖桶 vs 漏斗:
- 漏斗:以恆定速率處理
- 權杖桶:允許突發流量(消耗累積的權杖)
動態限流#
根據後端服務的回應時間動態調整:
1. 計算過去 N 秒請求的 P99 回應時間
2. 如果 P99 超過閾值,降低 QPS(如減半)
3. 慢啟動恢復,類似 TCP 擁塞控制
4. 在最佳值附近小幅震盪動態限流需要使用蓄水池抽樣 (Reservoir Sampling) 等近似演演算法來高效計算 P99。
降級設計 (Degradation)#
當資源不足或訪問量過大時,暫時犧牲部分功能保障核心功能。
降級策略#
1. 降低一致性#
強一致性 → 最終一致性
同步處理 → 非同步處理
例:電商下單
- 正常:同步扣庫存 + 同步支付
- 降級:非同步處理 + 貨到付款2. 停止次要功能#
優先級:核心功能 > 次要功能
例:電商系統
- 必須保留:下單、支付、物流
- 可停止:評論、推薦、搜尋3. 簡化功能#
完整版 → 精簡版
例:商品詳情頁
- 正常:商品資訊 + 評論 + 推薦
- 降級:只回傳商品資訊降級設計要點#
- 預先設計:降級邏輯要事先設計好,不能臨時寫
- 開關控制:通過組態開關快速切換
- 友好提示:告知用戶系統繁忙,請稍後再試
- 定期演練:降級功能平時不用,可能會有 bug
重試設計 (Retry)#
重試的語義是「認為這是暫時性故障」。如果是永久性錯誤,重試沒有意義。
適合重試的場景#
- 呼叫逾時
- 被呼叫端回傳可重試的錯誤(繁忙中、流控中、維護中)
不適合重試的場景#
- 業務級錯誤(無權限、非法資料)
- 技術性錯誤(HTTP 503 等)
重試策略#
指數退避 (Exponential Backoff)#
long getWaitTime(int retryCount) {
return (long) Math.pow(2, retryCount) * 100; // ms
}
// 第 1 次重試: 200ms
// 第 2 次重試: 400ms
// 第 3 次重試: 800ms
// ...重試策略類型#
| 策略 | 說明 |
|---|---|
| NeverRetry | 不重試 |
| AlwaysRetry | 無限重試(危險!可能死循環) |
| SimpleRetry | 固定次數重試 |
| TimeoutRetry | 逾時時間內重試 |
| CircuitBreakerRetry | 帶熔斷的重試 |
重試設計要點#
- 設置最大重試次數:避免無限重試
- 使用退避策略:避免加重負載
- 確保冪等性:重複執行結果相同
- 結合熔斷:超過重試次數後觸發熔斷
冪等性設計 (Idempotency)#
f(x) = f(f(x))一次和多次請求產生的副作用相同。
為什麼需要冪等性#
分散式呼叫有三種結果:
- 成功 (Success)
- 失敗 (Failed)
- 逾時 (Timeout) ← 這個狀態不確定!
逾時時,完全不知道請求是否被處理過,重試可能導致重複處理。
實現方式#
全局唯一 ID#
使用 Snowflake 等演演算法生成全局唯一的交易 ID。
處理流程#
-- 方式一:插入檢查衝突
INSERT INTO transactions (id, ...) VALUES (...)
ON DUPLICATE KEY UPDATE ...
-- 方式二:更新時檢查狀態
UPDATE orders
SET status = 'paid'
WHERE id = ? AND status = 'unpaid'HTTP 方法的冪等性#
| 方法 | 冪等性 | 說明 |
|---|---|---|
| GET | 是 | 獲取資源,無副作用 |
| HEAD | 是 | 同 GET,只回傳標頭 |
| OPTIONS | 是 | 獲取支援的方法 |
| PUT | 是 | 創建或更新資源 |
| DELETE | 是 | 刪除資源 |
| POST | 否 | 創建資源,多次呼叫會創建多個 |
彈力設計總結#
mindmap
root((彈力設計))
冗餘服務
負載均衡 + 健康檢查
服務發現 + 動態路由
Kubernetes 調度
服務解耦
Bulkheads 隔離
非同步通訊
補償事務
容錯設計
重試 + 熔斷 + 冪等
一致性處理
限流 + 降級彈力設計需要自動化運維工具的配合。沒有監控和自動化,再好的設計也難以發揮作用。建議使用:
- APM 服務監控
- Docker + Kubernetes 進行服務調度
- Spring Cloud 開發框架