Day 13,我們替外部呼叫加入 Timeout、Backoff 與 Idempotency。
這些控制可以處理短暫故障,但不能創造不存在的容量。如果付款服務持續停止回應,或瞬間流量超過系統上限,不斷 Retry 只會增加負載。
系統這時需要回答三個問題:
今天會建立一個本機 Demo,實測三種機制:
Health Check 不能只回答「Process 是否存在」。
一個 Node.js Process 可能仍在執行,但 Database Connection 已經中斷。它也可能卡在大量請求中,無法在合理時間內完成新工作。
常見 Health Check 可以分成三類:
| 類型 | 回答的問題 | 常見用途 |
|---|---|---|
| Startup | 應用程式是否完成啟動? | 避免啟動期間被誤判為故障 |
| Liveness | Process 是否仍能執行基本工作? | 判斷是否需要重新啟動 |
| Readiness | Instance 是否適合接收新流量? | 決定 Load Balancer 是否繼續送流量 |
這三種檢查的處理方式不同。
Liveness 失敗通常會觸發重新啟動。Readiness 失敗則應先停止新流量,讓 Instance 保留時間恢復或完成既有請求。
如果 Liveness 檢查所有外部依賴,Database 短暫故障可能讓所有 Instance 同時重新啟動。這會增加啟動流量,也可能把原本的 Dependency 故障放大成整個服務故障。
今天的程式放在:
demo-app/capacity-demo/scenario.js
進入 Demo:
cd /media/mickey/777/ithome/demo-app
執行:
npm run capacity:demo
腳本會模擬:
所有服務與資料都只存在於本機記憶體。
Demo 的 Liveness 回應:
/live 200 {"status":"alive"}
它只證明 Process 能接收請求並產生回應。
Liveness Endpoint 應符合以下條件:
如果 Endpoint 每次都查詢 Database、Payment、Email 與 Gemini,它本身可能變成額外負載,也會讓單一 Dependency 控制整個服務是否重新啟動。
Payment 故障時,Demo 回傳:
/ready 200 {
"status":"degraded",
"checks":{
"database":"up",
"payment":"down"
}
}
Database 故障時則回傳:
/ready database-down 503 {
"status":"not_ready",
"checks":{
"database":"down"
}
}
為什麼 Payment 故障時仍回傳 200?
因為這個 Demo 還能提供 Catalog。Payment 只影響 Checkout,Database 則是目前所有核心請求都需要的 Dependency。
這不是通用規則。
如果產品只有付款功能,Payment 就是核心依賴,Readiness 應回傳失敗。如果不同功能部署在不同服務,也可以只把 Checkout Service 移出流量。
團隊要先定義「Ready」代表哪些使用者流程可以運作,不能把所有 Dependency 放進同一份固定清單。
內部 Readiness Endpoint 可以提供元件狀態,協助平台決定是否送流量。
公開 Endpoint 則不應列出:
攻擊者不需要知道每一個內部依賴。Health Check 只要提供平台與值班人員需要的資訊。
Health Check 的網路存取範圍也應受到限制。Google Cloud Load Balancer 會由特定 Prober 發出檢查,Firewall 必須允許正確來源與 Port,但不需要向所有網路公開管理資訊。
Payment 故障時,Demo 仍回傳 Catalog:
/catalog 200 {
"products":3,
"source":"cache",
"stale":true
}
資料來自 Cache,而且可能不是最新版本。Response 明確標記 stale: true。
這就是 Graceful Degradation。系統降低功能或資料品質,但仍保留部分使用者價值。
常見降級方式包含:
降級不能欺騙使用者。
付款沒有完成時,系統不能顯示「付款成功」。資料不是最新版本時,也不應假裝內容仍是即時結果。
前兩次 Checkout 仍嘗試呼叫 Payment:
/checkout request=1 status=503 code=PAYMENT_DEPENDENCY_FAILURE
/checkout request=2 status=503 code=PAYMENT_DEPENDENCY_FAILURE
連續失敗達到門檻後,Circuit Breaker 開啟:
/checkout request=3 status=503 code=CHECKOUT_TEMPORARILY_UNAVAILABLE
/checkout request=4 status=503 code=CHECKOUT_TEMPORARILY_UNAVAILABLE
Payment dependency calls: 2
Circuit state: OPEN
總共有四次 Checkout,卻只呼叫 Payment 兩次。
Circuit Breaker 開啟後,後續請求快速失敗。這可以減少:
完整 Circuit Breaker 通常有三種狀態:
Circuit Breaker 需要 Metric 與 Alert。否則系統可能長時間停在 Open,團隊卻不知道 Checkout 已經關閉。
每秒 100 個簡單 Health Check,可能比每秒 5 個大型 AI Review 更便宜。
Google SRE 指出,不同請求的資源成本可能差異很大。只用 Queries Per Second 估算容量,容易忽略:
對 Production Readiness Agent 來說,一個只包含 20 行規則的 Review,與掃描大型 Repository 的成本不同。系統需要根據實際資源建立限制。
Demo 同時啟動五個工作,每個工作執行 30 毫秒。
沒有上限時:
Unbounded: completed=5 peak_in_flight=5
五個工作全部同時執行,Peak In-flight 是 5。
這次實驗規模很小,所以全部完成。正式環境如果同時進入 5,000 個大型 Review,Memory、CPU、Gemini Quota 或 Socket 可能先耗盡。
安全版本最多同時處理兩個工作:
Limited: accepted=2 rejected=3 peak_in_flight=2
超過上限的三個請求快速收到:
{
"status": 503,
"code": "OVER_CAPACITY",
"retryAfterSeconds": 1
}
這個結果表面上比較差,因為三個請求遭到拒絕。
但服務保住了兩個可完成的工作,也把 Peak In-flight 固定在 2。如果系統接受所有請求後一起耗盡資源,最後可能五個都失敗。
Load Shedding 的目標不是讓每個請求成功,而是讓系統在超載期間繼續完成可控制的工作量。
另一種做法是把三個請求放進 Queue。
Queue 適合可以延後完成的工作,例如:
但 Queue 必須設定:
如果 Queue 沒有上限,系統只是把 Memory Exhaustion 延後發生。
互動式 Checkout 已經超過使用者 Deadline 時,繼續排隊也沒有價值。系統應快速失敗,讓使用者稍後再試。
降級功能平常很少執行,因此最容易在真正事故時壞掉。
Google SRE 建議定期讓少量服務進入接近過載的狀態,以確認 Graceful Degradation 仍能運作。
團隊至少要測試:
沒有演練過的 Fallback,只是一項尚未驗證的假設。
Production Readiness Agent 會依賴 Gemini。Gemini 暫時不可用時,不應讓整個產品只剩空白畫面。
可以設計三層結果:
系統要清楚標記結果來源:
{
"reviewMode": "static-only",
"geminiStatus": "unavailable",
"complete": false
}
固定規則掃描不能假裝成完整 AI Review。使用者要知道哪些檢查尚未完成。
大型 Repository 還需要限制:
Agent 應檢查:
一項可操作的 Finding 可以寫成:
問題:AI Review 沒有併發上限
證據:每個 Request 都立即建立新的 Gemini Review 工作
觸發條件:大量使用者同時提交 Repository
影響:Memory 與 Gemini Quota 耗盡,所有 Review 延遲或失敗
驗證:送出五個同時請求,Peak In-flight 增加到 5
修正:限制同時工作數,設定有上限 Queue,超量時回傳 OVER_CAPACITY
Agent 不能只看到 Promise.all() 就判定服務一定會過載。它還要確認輸入數量是否受控、每項工作成本,以及上層是否已經限制併發。
Health Check 告訴平台 Instance 是否能繼續運作與接收流量。Graceful Degradation 在 Dependency 故障時保留部分功能。Load Shedding 則在超過容量時保護仍可完成的工作。
今天的實驗得到四個結果:
明天,我們會處理備份、資料庫遷移、復原與回滾。服務即使能撐過流量,錯誤部署仍可能破壞資料。