Spring Cloud CircuitBreaker服务熔断+隔离+限流
目录
- 介绍
- 实现原理
- 服务熔断 (CircuitBreaker)
- 基于计数的滑动窗口 (COUNT_BASED) 案例
- 断路器开启/关闭的条件
- 服务隔离 (BulkHead)
- 基于信号量 (SemaphoreBulkhead) 案例
- 服务限流 (RateLimiter)
- 令牌桶算法 (Token Bucket) 案例
- 总结
介绍
Spring Cloud CircuitBreaker 是 Spring Cloud 提供的熔断器实现框架,用于增强微服务系统的稳定性和容错能力。它通过熔断机制,在服务调用失败或延迟过高时快速失败,防止故障扩散。
实现原理
CircuitBreaker 的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性。
断路器状态:
- Closed: 服务调用正常,断路器处于关闭状态。
- Open: 服务调用失败次数超过阈值,断路器打开,所有请求直接失败。
- HAIF_OPEN: 经过一段时间后,断路器进入半开状态,允许部分请求通过以测试服务是否恢复。
原理: 当一个组件或服务出现故障时,CircuitBreaker 会迅速切换到开放 OPEN 状态(保险丝跳闸断电),避免更多的请求发送到该组件或服务。这可以减少对该组件或服务的负载,防止该组件或服务进一步崩溃,并使整个系统能够继续正常运行。同时 CircuitBreaker 还可以提高系统的可用性和健壮性,因为它可以在分布式系统的各个组件之间自动切换,从而避免单点故障的问题。
服务熔断 (CircuitBreaker)
滑动窗口类型有两种,推荐使用基于计数的滑动窗口 (COUNT_BASED)。
- 基于计数的滑动窗口 (COUNT_BASED)
- 基于时间的滑动窗口 (TIME_BASED)
基于计数的滑动窗口 (COUNT_BASED) 案例
支付模块 cloud-payment8001:PayCircuitController
@RestController
public class PayCircuitController {// Resilience4j CircuitBreaker@GetMapping("/pay/circuit/{id}")public String myCircuitBreaker(@PathVariable("id") Integer id) {if (id < 0) {throw new RuntimeException("circuit id 不能为负数!");}if (id == 999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello circuit! inputId: " + id;}
}
通用模块 cloud-common-api:PayFeignApi
@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/circuit/{id}")public String myCircuitBreaker(@PathVariable("id") Integer id);
}
订单模块 cloud-feign-order9002 引入依赖
<!-- Resilience4j CircuitBreaker -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- 由于断路保护需要AOP实现,所以必须导入AOP包 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
订单模块 cloud-feign-order9002 配置 yml
spring:cloud:openfeign:circuitbreaker: # 开启circuitbreaker和分组激活enabled: truegroup:enabled: true # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# Resilience4j CircuitBreaker 按照次数:COUNT_BASED
# 6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
# 等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
# 如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态,恢复正常处理请求。
resilience4j:circuitbreaker:configs:default:failureRateThreshold: 50 # 设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker 变为OPEN状态。slidingWindowType: COUNT_BASED # 滑动窗口的类型slidingWindowSize: 6 # 滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒minimumNumberOfCalls: 6 # 断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker 将自动从开启状态过渡到半开状态,并允许一些请求通过,以测试服务是否恢复正常waitDurationInOpenState: 5s # 从OPEN到HALF_OPEN状态需要等待的时间permittedNumberOfCallsInHalfOpenState: 2 # 半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker 将允许最多 permittedNumberOfCallsInHalfOpenState 个请求通过,如果其中有任何一个请求失败,CircuitBreaker 将重新进入开启状态。recordExceptions:- java.lang.Exceptioninstances:cloud-payment-service: # 指定服务baseConfig: default
订单模块 cloud-feign-order9002:OrderCircuitController
@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/circuit/{id}")@CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback")public String myCircuitBreaker(@PathVariable("id") Integer id) {return payFeignApi.myCircuitBreaker(id);}// myCircuitFallback 就是服务降级后的兜底处理方法public String myCircuitFallback(Integer id, Throwable t) {// 这里是容错处理逻辑return "myCircuitFallback: 系统繁忙,请稍后再试!";}
}
测试结果
A: 调用 http://localhost:9002/feign/pay/circuit/8,返回 Hello circuit! inputId: 8
B: 调用 http://localhost:9002/feign/pay/circuit/-9,返回 myCircuitFallback: 系统繁忙,请稍后再试!
操作步骤: 执行A => B => A => B => A => B,达到6次且50%调用失败,断路器开启,接着执行A,此时返回 myCircuitFallback: 系统繁忙,请稍后再试!等待5s后再次执行A,此时返回 Hello circuit! inputId: 8,服务恢复正常。
断路器开启/关闭的条件
1. 当满足一定的峰值和失败率达到一定条件后,断路器将会进入 OPEN 状态(保险丝跳闸),服务熔断。
2. 当进入 OPEN 状态时,所有请求都不会调用主业务逻辑方法,而是直接走fallbackmetnod 兜底方法,服务降级。
3.一段时间之后断路器会从 OPEN 状态进入 HALF_OPEN 半开状态,先放几个请求过去探探链路是否通,若成功,则断路器进入 CLOSE (类似保险丝闭合,恢复可用) 状态;若失败,则继续开启。
服务隔离 (BulkHead)
Resilience4j 提供了如下两种隔离的实现方式,可以限制并发执行的数量。
- 基于信号量 (SemaphoreBulkhead)
- 基于固定线程池 (FixedThreadPoolBulkhead)
基于信号量 (SemaphoreBulkhead) 案例
支付模块 cloud-payment8001:PayCircuitController
@RestController
public class PayCircuitController {// Resilience4j BulkHead@GetMapping("/pay/bulkhead/{id}")public String myBulkhead(@PathVariable("id") Integer id) {if (id < 0) {throw new RuntimeException("bulkhead id 不能为负数!");}if (id == 999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello bulkhead! inputId: " + id;}
通用模块 cloud-common-api:PayFeignApi
@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/bulkhead/{id}")public String myBulkhead(@PathVariable("id") Integer id);
}
订单模块 cloud-feign-order9002 引入依赖
<!-- Resilience4j BulkHead -->
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId>
</dependency>
订单模块 cloud-feign-order9002 配置 yml
spring:cloud:openfeign:circuitbreaker: # 开启circuitbreaker和分组激活enabled: truegroup:enabled: true # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# Resilience4j BulkHead
resilience4j:bulkhead:configs:default:maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量maxWaitDuration: 1s # 当达到并发调用数量时,新线程的阻塞时间,只等待1s,过时执行兜底fallback方法instances:cloud-payment-service: # 指定服务baseConfig: default
订单模块 cloud-feign-order9002:OrderCircuitController
@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/bulkhead/{id}")@Bulkhead(name = "cloud-payment-service", fallbackMethod = "myBulkheadFallback", type = Bulkhead.Type.SEMAPHORE)public String myBulkhead(@PathVariable("id") Integer id) {return payFeignApi.myBulkhead(id);}public String myBulkheadFallback(Throwable t) {return "myBulkheadFallback: 系统繁忙,请稍后再试!";}
}
测试结果
A: 调用 http://localhost:9002/feign/pay/bulkhead/8,返回 Hello bulkhead! inputId: 8
B: 调用 http://localhost:9002/feign/pay/bulkhead/999,等待5s后返回 Hello bulkhead! inputId: 999
操作步骤: 连续执行B两次,达到允许并发线程数2,服务隔离,接着执行A (前两次B都处于执行中这段时间内),返回 myBulkheadFallback: 系统繁忙,请稍后再试!等待某一次B正常返回后,再次执行A,返回 Hello bulkhead! inputId: 8,服务恢复正常。
服务限流 (RateLimiter)
常见的限流算法:
- 漏斗算法
- 令牌桶算法
- 滚动时间窗口
- 滑动时间窗口
Resilience4j 的 RateLimiter 默认采用令牌桶算法。
令牌桶算法 (Token Bucket) 案例
支付模块 cloud-payment8001:PayCircuitController
@RestController
public class PayCircuitController {// Resilience4j RateLimiter@GetMapping("/pay/ratelimiter/{id}")public String myRateLimiter(@PathVariable("id") Integer id) {return "Hello, ratelimiter! inputId: " + id;}
通用模块 cloud-common-api:PayFeignApi
@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/ratelimiter/{id}")public String myRateLimiter(@PathVariable("id") Integer id);
}
订单模块 cloud-feign-order9002 引入依赖
<!-- Resilience4j RateLimiter-->
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-ratelimiter</artifactId>
</dependency>
订单模块 cloud-feign-order9002 配置 yml
spring:cloud:openfeign:circuitbreaker: # 开启circuitbreaker和分组激活enabled: truegroup:enabled: true # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# Resilience4j RateLimiter
resilience4j:ratelimiter:configs:default:limitForPeriod: 2 # 在一次刷新周期内,允许执行的最大请求数limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriodtimeout-duration: 1 # 线程等待权限的默认等待时间instances:cloud-payment-service: # 指定服务baseConfig: default
订单模块 cloud-feign-order9002:OrderCircuitController
@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/ratelimiter/{id}")@RateLimiter(name = "cloud-payment-service", fallbackMethod = "myRateLimiterFallback")public String myRateLimiter(@PathVariable("id") Integer id) {return payFeignApi.myRateLimiter(id);}public String myRateLimiterFallback(Integer id, Throwable t) {return "myRateLimiterFallback: 已被限流,请稍后再试!";}
}
测试结果
A: 调用 http://localhost:9002/feign/pay/ratelimiter/8,返回 Hello ratelimiter! inputId: 8
操作步骤: 连续多次快速执行A,1s内达到允许最大请求数2,服务限流,返回 myRateLimiterFallback: 已被限流,请稍后再试!降低频率执行A,返回 Hello ratelimiter! inputId: 8,服务恢复正常。
总结
以上主要介绍了 Spring Cloud CircuitBreaker 服务熔断、隔离、限流的相关知识,想了解更多 Spring Cloud CircuitBreaker 知识的小伙伴请参考 Spring Cloud CircuitBreaker 官网 进行学习,学习更多 Spring Cloud 实战实用技巧的小伙伴,请关注后期发布的文章,认真看完一定能让你有所收获。