负载均衡、超时与重试:一次调用怎样变成故障放大器
把实例列表、选择算法、连接池、端到端 Deadline、重试条件、重试风暴和反馈信号连成一条调用控制链。
Spring Cloud LoadBalancer 通过 ServiceInstanceListSupplier 提供实例列表,并以独立子上下文承载每个 serviceId 的配置。参见 Spring Cloud LoadBalancer。
负载均衡只在当前候选集中做局部选择
轮询追求请求数量均匀,随机在大样本下均衡,权重表达静态容量,最少并发或延迟感知尝试使用运行反馈。算法无法修复错误实例列表,也不知道请求成本是否相同。一个批量报表与一个轻量查询都计作一次时,轮询可能让负载极不均匀。
实例列表来自发现缓存,选择后还要获取连接。连接池若按主机维度限制,扩容新实例时需要预热;DNS、TLS 和 HTTP/2 多路复用会改变连接成本。粘性会话把状态绑定实例并降低均衡能力,除非有明确本地状态收益,否则优先把会话外置或用一致性 key 做局部路由。
超时必须从入口向下游递减
一次请求包含入口排队、业务计算、连接池等待、连接建立、写入、服务端排队、处理和读取响应。只设置 socket read timeout 会遗漏前面所有等待。入口 deadline 应减去已消耗时间和安全余量传给下游,每一层不能重新设置完整 2 秒,否则五层调用可能等待十秒。
连接超时应较短并区分无路由与实例拒绝;读取超时根据操作尾延迟和业务截止时间;连接池等待必须有上限,防止资源耗尽后线程永久堆积。超时指标要标记发生阶段,才能判断是池、网络、服务端还是下游慢。
重试乘法来自每一层都自称负责可靠
入口、Feign、HTTP client 和数据库适配器各重试两次,最坏尝试数不是六次而是指数乘积。只有最接近失败且掌握幂等语义的一层执行重试,其他层传播结果。重试服从原 deadline、最大尝试、总等待、并发和租户预算,并使用抖动退避。
负载均衡重试应优先换实例,但同一业务写操作换实例仍可能重复执行。被动失败要反馈给实例选择,避免每次都撞向刚刚失败的节点;同时防止少量超时永久隔离其实健康的慢实例。恢复使用有限探测而不是瞬间放回全部流量。
把同步调用画成预算递减的状态机
设计时为每条边记录 owner、协议、连接模型、超时阶段、重试责任、幂等键、流量上限、降级语义和回滚方式。同步扇出越大,成功率乘积越低,尾延迟取最大值;能异步解耦的非关键副作用不要停留在主请求链。
失败控制必须按依赖隔离
注册中心不可用、无实例、连接池满、连接失败、读取超时、业务拒绝和服务端错误要分型。只有动作未开始或有幂等保障的暂态失败允许重试;结果未知先查询或使用稳定幂等键。限流、隔离和熔断的拒绝应成为可观测结果,不能统一包装成空数据。
Hedging 不是无代价的尾延迟优化
对可幂等读取,可在首个尝试超过分位阈值后向另一实例发起对冲,以先完成者为准。它能降低少量异常慢实例造成的尾延迟,却会增加下游流量并让热点查询重复。对冲只能占用独立小预算、限制比例并取消输掉的尝试;写操作没有幂等与资源保护时禁止使用。
自适应算法需要防止反馈回路。延迟升高后把流量全部赶到其他实例,可能让健康实例过载,再来回震荡。采样窗口、衰减、最小流量和恢复步长要显式,实例启动预热期间也不能因样本少被判断为最快而瞬间接满流量。
用两个 Java 状态模型固定决策边界
这两个 Java 17 模型不启动真实注册中心、Gateway 或 RPC Server,而是把本篇的状态、预算和不变量转成确定输出。真实集成测试继续验证客户端协议、自动配置、连接复用、推送延迟与故障时序。
javac --release 17 -Xlint:all -Werror examples/backend-development/microservice/loadbalance-timeout-retry/RetryAmplificationDemo.java examples/backend-development/microservice/loadbalance-timeout-retry/DeadlineBudgetDemo.java
java -cp examples/backend-development/microservice/loadbalance-timeout-retry RetryAmplificationDemo
java -cp examples/backend-development/microservice/loadbalance-timeout-retry DeadlineBudgetDemolayers=3 retriesPerLayer=2 worstCaseAttempts=8 amplification=8
totalMillis=500 queue=40 connect=60 attempt1=220 remaining=180 retryAllowed=true retryCapMillis=150状态模型输出变化时,要解释对应的服务边界、Deadline、版本或治理不变量为什么改变。集成测试必须主动制造连接已写出后超时、实例列表陈旧、配置刷新一半、半开探测和灰度标签断链。
契约、安全与配置共同决定可运营性
动态配置和发现元数据都可能陈旧。变更必须带版本、owner、范围、过期时间和回滚值,先灰度到少量实例并比较版本分布。会影响线程池、连接池、路由、限流和鉴权的配置要通过不变量校验,失败时保留上一完整快照。
容量与可观测证据要覆盖控制面和数据面
上线证据包括新旧契约互调、实例排空、Deadline 传播、幂等重试、治理状态机、灰度指标和回滚后的残留任务。完成标准不是“组件控制台显示健康”,而是业务不变量保持、未知结果可查询、故障被限制在预算内并最终收敛。
