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",定期进行混沌测试。