第 5 章 · Spring Cloud LoadBalancer 与 Resilience4j 熔断降级
本章目标:深入 Spring Cloud LoadBalancer 实例选择策略与缓存;在 OpenFeign 与 Gateway 上集成 Resilience4j 实现熔断、限流、超时、舱壁;设计 fallback 降级返回;理解雪崩效应与半开状态;在 svc-spring-demo 中模拟 product-svc 故障时的订单创建体验。
学时建议:5~6 小时(含 2 小时故障注入实验)
前置:spring-cloud-web ch01~ch04;spring-boot-web ch10 异步与超时概念。
5.1 为什么需要客户端负载均衡与熔断
product-svc 实例
┌──────┐ ┌──────┐
order-svc ─────►│ :8082│ │ :8092│
└──────┘ └──────┘
│
若 8082 慢/挂,仍被轮询到?
▼
LoadBalancer 选健康实例
+
CircuitBreaker 快速失败 + fallback
| 问题 | 组件 |
|---|---|
| 多实例选哪一个 | LoadBalancer |
| 下游持续失败拖垮调用方 | CircuitBreaker 熔断 |
| 仍需有限流量试探恢复 | 半开 Half-Open |
| 给用户可理解响应 | Fallback 降级 |
虚构项目 svc-spring-demo;故障实验仅在本地进行,勿对共享环境注入混沌。
5.2 Spring Cloud LoadBalancer 基础
Spring Cloud 2020 起,Ribbon 已移除,默认 Spring Cloud LoadBalancer。
5.2.1 与 Nacos 集成
Feign @FeignClient(name = "product-svc") 时:
- 从 Nacos 拉取
product-svc实例列表 - LoadBalancer 按策略选一个
- 发起 HTTP 请求
无需额外 @LoadBalanced 注解(Feign 内置);RestTemplate 需:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
5.2.2 负载均衡策略
| 策略 | Bean 名 | 行为 |
|---|---|---|
| 轮询(默认) | RoundRobinLoadBalancer | 依次选择 |
| 随机 | 自定义 ReactorLoadBalancer | 随机实例 |
| 同 zone 优先 | 配置 spring.cloud.loadbalancer.zone | 减少跨机房延迟 |
自定义轮询 + 权重(Nacos 元数据 weight)需扩展 ServiceInstanceListSupplier(进阶选修)。
5.2.3 实例缓存
spring:
cloud:
loadbalancer:
cache:
enabled: true
ttl: 35s
缓存减少对 Nacos 的压力;实例上下线可能有短暂延迟,可配合 shorter TTL。
5.3 Resilience4j 简介
Resilience4j 是轻量级容错库,Spring Cloud Circuit Breaker 抽象默认推荐实现之一。
| 模块 | 作用 |
|---|---|
circuitbreaker | 熔断 |
retry | 重试(与 ch04 Feign Retry 二选一或组合谨慎) |
ratelimiter | 限速 |
bulkhead | 舱壁隔离并发 |
timelimiter | 超时 |
调用 product-svc
│
▼
CircuitBreaker(统计失败率)
│ closed:正常
│ open:直接 fallback
│ half-open:放行少量探测
▼
Feign HTTP
5.4 依赖与配置
5.4.1 order-svc 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
5.4.1b gateway-svc 依赖(Gateway 熔断必加)
Gateway 基于 WebFlux,须使用 Reactor 版熔断器,不能只依赖 Servlet 栈的 spring-cloud-starter-circuitbreaker-resilience4j:
<!-- gateway-svc/pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
5.4.2 application 配置
resilience4j:
circuitbreaker:
instances:
product-svc: # 实例名须与 Feign 熔断名一致(见 5.5.1 命名解析器)
slidingWindowSize: 20
minimumNumberOfCalls: 10
failureRateThreshold: 50
waitDurationInOpenState: 30s
permittedNumberOfCallsInHalfOpenState: 5
automaticTransitionFromOpenToHalfOpenEnabled: true
timelimiter:
instances:
product-svc:
timeoutDuration: 5s
spring:
cloud:
openfeign:
circuitbreaker:
enabled: true
| 参数 | 含义 |
|---|---|
slidingWindowSize | 统计窗口调用次数 |
failureRateThreshold | 失败率达 50% 打开熔断 |
waitDurationInOpenState | 开路 30s 后转半开 |
5.5 Feign + CircuitBreaker 集成
5.5.1 启用与熔断实例命名
spring:
cloud:
openfeign:
circuitbreaker:
enabled: true
Feign 调用触发熔断时,Resilience4j 实例名默认为 Client名#方法签名(如 product-svc#getById(Long)),逐个方法配置实例不现实。注册 CircuitBreakerNameResolver(Spring Cloud OpenFeign 3.1+)统一改为按 Client 名共享一个熔断实例,与 5.4.2 的 product-svc 配置对应:
@Configuration
public class FeignCircuitBreakerConfig {
@Bean
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
// 熔断/限流实例名 = Feign Client 名,该 Client 的所有方法共用一个熔断器
return (feignClientName, target, method) -> feignClientName;
}
}
注意:spring.cloud.openfeign.client.config.*下没有circuitBreakerName这个配置项,网上部分教程的写法不会生效。命名只能由CircuitBreakerNameResolver控制。
Feign Client:
@FeignClient(
name = "product-svc",
path = "/api/v1/products",
fallbackFactory = ProductClientFallbackFactory.class
)
public interface ProductClient {
@GetMapping("/{id}")
Result<ProductDto> getById(@PathVariable("id") Long id);
}
5.5.2 FallbackFactory
@Component
@Slf4j
public class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {
@Override
public ProductClient create(Throwable cause) {
return new ProductClient() {
@Override
public Result<ProductDto> getById(Long id) {
log.warn("ProductClient fallback id={} reason={}", id, cause.toString());
return Result.fail(ErrorCode.PRODUCT_SERVICE_DEGRADED);
}
@Override
public Result<PageResult<ProductDto>> list(int page, int size) {
return Result.fail(ErrorCode.PRODUCT_SERVICE_DEGRADED);
}
};
}
}
降级策略:
| 场景 | fallback 行为 |
|---|---|
| 商品详情 | 返回「服务繁忙,请稍后」 |
| 列表页 | 返回缓存快照(spring-boot-web ch10 Redis,选修) |
| 下单关键路径 | 明确失败,禁止静默成功 |