Timeout и circuit control
1)為什麼需要它
系統不會從一個「致命」故障中掉落,而是從延遲和「波浪」回避的積累中掉落。Taymauts限制了等待時間並釋放了資源,並且電路控制(breaker+shedding+自適應競爭)不允許降解在整個依賴鏈中傳播。目的是將p95/p99保持在目標範圍內,並在部分故障的情況下保持可用性。
2)基本定義
2.1 Taimout物種(L7/L4)
連接時間-建立TCP/TLS連接。
TLS/手動計時-握手TLS/HTTP2握手。
Write timeout-發送請求(包括主體)。
閱讀時間-等待響應和/或整個主體的第一個字節。
Idle/Keep-Alive timeout是非活動連接。
過期是整個請求(終結)的「硬」截止日期。
2.2 Taymout預算(最後期限預算)
我們突出顯示目標「deadline_total」並按階段劃分:- `ingress (gateway) + authZ + app + DB/cache + outbound PSP`.
- 網關:30毫秒,
- 附件:120毫秒、
- DB:120毫秒,
- PSP:100毫秒,
- 庫存:30毫秒。
2.3 Propagation和取消
「deadline」/「timeout」必須沿鏈條向下傳遞(上下文、標題、gRPC Deadline)。過期時-取消後臺操作(abort/ctx cancel),清除鎖定/信號量。
3)定時設置策略
1.自上而下:基於SLO和p95-設置端到端末端,然後分解為下端。
2.識別「昂貴」路徑(文件下載、報告、外部PSP)-單獨的較長但有限。
3.Idempotent vs write:
idempotent (GET/狀態重播)-更短,更具攻擊性;
寫作/現金-稍長,但具有一次重播和偶數。
4.按計劃/tenant進行分級(企業可能比時間更長,但並行性更小)。
4)電路斷路器: 模型和參數
4.1個觸發策略
失敗率:在N查詢/時間窗口中錯誤百分比≥ X%。
Consecutive failures: M連敗。
慢通率:呼叫比例超過閾值T。
錯誤類:taymouts/5xx/connection-reset →「致命」,4xx-不考慮。
4.2個狀態
Closed-跳過一切,積累統計數據。
Open是即時故障(節省資源,不施加依賴性)。
半開放是用於「水檢查」的小型「樣本」(N查詢)。
4.3有用的補充
Bulkhead (spangouts):每個依賴項的流/連接池,這樣一個不會「吸入」所有內容。
自適應耦合:通過觀察到的潛伏期自動並發約束(AIMD/維加斯樣算法)。
當本地資源短缺(隊列、CPU、GC暫停)時,Load Shedding:早期故障/降級。
5)互動: taymauts,retrai,限制
首先是截止日期,然後是轉發:每個重播都必須設置為共享截止日期。
Backoff+jitter進行重播;尊重「Retry-After」和retry-budget。
比例限制:打開決勝局-降低限制,以免加劇風暴。
Idempotency:在寫作操作中是強制性的(為了避免在"靜音";時間間隔時被擊中)。
在哪裏轉發:最好在邊緣(客戶端/網關)而不是深處。
6)實際目標(基準)
Public read API: end to end '200-500 ms'、read timeout '100-300 ms'。
關鍵寫作(付款):'300-800 ms'e2e;外部PSP ≤ 「250-400 ms」。
Connect/TLS: 「50-150 ms」(更多是網絡/解決問題)。
Idle:「30-90 s」(移動客戶為了節省電池而縮短了)。
根據p95/p99和區域調整值。
7)Configi和示例
7.1 Envoy (cluster+route, pseudo)
yaml clusters:
- name: payments_psp connect_timeout: 100ms type: STRICT_DNS lb_policy: ROUND_ROBIN circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 2000 max_requests: 2000 max_retries: 50 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50
routes:
- match: { prefix: "/api/v1/payments" }
route:
cluster: payments_psp timeout: 350ms # per-request deadline idle_timeout: 30s retry_policy:
retry_on: "reset,connect-failure,refused-stream,5xx,gateways"
num_retries: 1 per_try_timeout: 200ms
7.2個NGINX(周邊)
nginx proxy_connect_timeout 100ms;
proxy_send_timeout 200ms; # write proxy_read_timeout 300ms; # read (первый байт/все тело)
keepalive_timeout 30s;
send_timeout 15s;
Быстрый отказ при перегрузке limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 50;
7.3 gRPC(客戶端,Go-pseudo)
go ctx, cancel:= context.WithTimeout(context.Background(), 350time.Millisecond)
defer cancel()
resp, err:= client.Pay(ctx, req) // Deadline передается вниз
7.4個HTTP客戶端(Go)
go client:= &http.Client{
Timeout: 350 time.Millisecond, // общий дедлайн на запрос
Transport: &http.Transport{
TLSHandshakeTimeout: 100 time.Millisecond,
ResponseHeaderTimeout: 250 time.Millisecond,
IdleConnTimeout: 30 time.Second,
MaxIdleConnsPerHost: 100,
},
}
7.5 Resilience4j (Java, pseudo)
yaml resilience4j.circuitbreaker.instances.psp:
slidingWindowType: TIME_BASED slidingWindowSize: 60 failureRateThreshold: 50 slowCallDurationThreshold: 200ms slowCallRateThreshold: 30 permittedNumberOfCallsInHalfOpenState: 5 waitDurationInOpenState: 30s
resilience4j.timelimiter.instances.psp:
timeoutDuration: 350ms
8)可觀察性和變量
8.1個指標
`http_client_requests{endpoint, status}`, `client_latency_bucket`
8.2 Traces
Spans:ingress → handler → DB/Redis →外部。
屬性:「timeout_ms_target」,「circuit_state」,「queue_time_ms」。
Exemplars:將p99峰綁定到特定的trace-id。
8.3 Alerta
'p99_latency {critical}'>目標連續X分鐘。
「timeout_rate {dependency}」跳躍>Y%。
頻繁過渡到「open」/「flapping」 breaker。
在高CPU/GC下生長「shed_requests_total」。
9) Adaptive Concurrency & Load Shedding
9.1個想法
自動化降低了潛伏尾巴生長的並發性:- AIMD:緩慢增加,大幅下降。
- 拉斯維加斯般的:保持目標隊列(queue time)。
- 基於令牌:每個請求「燃燒」令牌;令牌按測得的速度發放。
9.2實現
沿路線的本地信號燈;目標是將「queue_time」保持在閾值以下。
網關上的全局「保險絲」(RPS/競爭力極限)。
在缺少CPU/連接的情況下,邏輯執行之前的早期故障(429/503帶有「Retry-After」)。
10)測試和混亂場景
Packet loss/dup/drop (tc/tbf, Toxiproxy).
Latency injection:人為增加50-300毫秒的依賴性。
Knob轉彎:減少連接池,將負載提高到飽和度。
Kill/Degrade 單個區域/shard(部分不可訪問)。
檢查:retray風暴是否「失敗」;breaker可以預見地打開;隊列是否在增長。
11)反模式
一個全局「read timeout」,沒有詳細說明connect/TLS/per階段。
缺乏共同的截止日期→撤退超出了SLO。
Retrai沒有jitter,沒有retry-budget。
沒有idle-taymout的「永恒」連接→描述符泄漏。
Breaker認為4xx是致命的錯誤。
沒有取消/abort →客戶定時後繼續背景工作。
對於移動/不穩定的網絡,時間過長。
12) iGaming/財務細節
關鍵寫作(存款/結論):與Idempotency-Key進行一次簡短的重播,然後「202接受」+投票而不是無限的期望。
PSP/銀行業務:按供應商/地區劃分的政策(有些較慢)。
負責任的付款和限制:在鎖定/咆哮時-快速「423/409」,不要拉伸「掛起」的交易。
報告/聚合-異步運行(batch+狀態資源)。
13)準備就緒支票清單
- 在關鍵路線(GET/POST)上定義端到端最後期限。
- 按階段分配預算;啟用了截止日期。
- 在網關和客戶端上連接/TLS/read/write/idle時間表。
- 帶有故障率閾值和慢通話閾值的電路斷路器;正確的半開放邏輯。
- Bulkheads成癮;每條路線的並發限制。
- 在超載時執行業務邏輯之前進行負載共享。
- 與retraff的集成:backoff+jitter,retry-budget,尊重「Retry-After」。
- write、「Idempotency-Key」和outbox for events.
- 度量標準:時間/慢通話/狀態breaker/queue時間/競爭力。
- 混沌測試:註射延遲/損失/幹擾,區域退化。
- 客戶文檔:時間表示例,響應代碼,重復提示。
14) TL;DR
給每個請求一個硬的截止日期,將其分解到各個階段,然後沿著鏈條傳播。通過circuit breaker+bulkheads+adaptive concurrency+load shedding管理故障。重播僅在截止日期內,帶有抖動和預算。write-只是偶然的。測量時間/慢通話,破發器狀態和「queue_time」,定期進行混沌測試。