Spring Cloud Circuit Breaker 学习
官方文档
https://docs.spring.io/spring-cloud-circuitbreaker/docs/current/reference/html/#usage-documentation
功能分析
配置入口
org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JAutoConfiguration
主要配置了 org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory以及 暴露监控信息
自定义 配置
-
定义 Customizer ,把它 添加载到spring 的bean。
-
定义 Customizer
-
我们还可以 在定义 Customizer 时候 给 Resilience4JCircuitBreakerFactory 添加 CircuitBreakerCustomizer (This can be useful for adding event handlers to Resilience4J circuit breakers.) ,给特定的CircuitBreaker 添加自定义配置,作用优先级 应该高于 resilience4j-spring-boot2 的属性配置
定义 @Bean
public Customizer<Resilience4JCircuitBreakerFactory> slowCustomizer() {
return factory -> factory.addCircuitBreakerCustomizer(circuitBreaker -> circuitBreaker.getEventPublisher()
.onError(normalFluxErrorConsumer).onSuccess(normalFluxSuccessConsumer), "normalflux");
}
Resilience4JCircuitBreakerFactory 中的 groupName
- 一组 spring 的 CircuitBreaker 共用一个 ExecutorService
@Override
public Resilience4JCircuitBreaker create(String id, String groupName) {
Assert.hasText(id, "A CircuitBreaker must have an id.");
Assert.hasText(groupName, "A CircuitBreaker must have a group name.");
final ExecutorService groupExecutorService = executorServices.computeIfAbsent(groupName,
group -> Executors.newCachedThreadPool());
return create(id, groupName, groupExecutorService);
}
- groupName 会 生成tags {group=${groupName} } 传给 timeLimiter,circuitBreaker, bulkheadProvider
- groupName 会作为 id 传给 bulkheadProvider (默认情形下groupName = ${id} )
Resilience4JCircuitBreaker 介绍
说明
- Resilience4JCircuitBreakerFactory create 方法得到 的是 Resilience4JCircuitBreaker
- Resilience4JCircuitBreaker 的 run 方法 组合 timeLimiter,circuitbreaker bulkhead
timeLimiter,circuitbreaker,bulkhead 的 组合
bulkheadProvider 存在时候
//org.springframework.cloud.circuitbreaker.resilience4j.Resilience4jBulkheadProvider#run
public <T> T run(String id, Supplier<T> toRun, Function<Throwable, T> fallback, CircuitBreaker circuitBreaker,
TimeLimiter timeLimiter, io.vavr.collection.Map<String, String> tags) {
Supplier<CompletionStage<T>> bulkheadCall = decorateBulkhead(id, tags, toRun);
final Callable<T> timeLimiterCall = decorateTimeLimiter(bulkheadCall, timeLimiter);
final Callable<T> circuitBreakerCall = circuitBreaker.decorateCallable(timeLimiterCall);
return Try.of(circuitBreakerCall::call).recover(fallback).get();
}
bulkheadProvider 不存在时候
// org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker#run
@Override
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
final io.vavr.collection.Map<String, String> tags = io.vavr.collection.HashMap.of(CIRCUIT_BREAKER_GROUP_TAG,
this.groupName);
TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter(id, timeLimiterConfig, tags);
Supplier<Future<T>> futureSupplier = () -> executorService.submit(toRun::get);
Callable restrictedCall = TimeLimiter.decorateFutureSupplier(timeLimiter, futureSupplier);
io.github.resilience4j.circuitbreaker.CircuitBreaker defaultCircuitBreaker = registry.circuitBreaker(this.id,
this.circuitBreakerConfig, tags);
circuitBreakerCustomizer.ifPresent(customizer -> customizer.customize(defaultCircuitBreaker));
if (bulkheadProvider != null) {
...
}
else {
Callable<T> callable = io.github.resilience4j.circuitbreaker.CircuitBreaker
.decorateCallable(defaultCircuitBreaker, restrictedCall);
return Try.of(callable::call).recover(fallback).get();
}
}
配置介绍
{
"properties": [
{
"defaultValue": "true",
"name": "spring.cloud.circuitbreaker.resilience4j.enabled",
"description": "Enables Resilience4J auto-configuration.",
"type": "java.lang.Boolean"
},
{
"defaultValue": "true",
"name": "spring.cloud.circuitbreaker.resilience4j.blocking.enabled",
"description": "Enables blocking Resilience4J auto-configuration.",
"type": "java.lang.Boolean"
},
{
"defaultValue": "true",
"name": "spring.cloud.circuitbreaker.resilience4j.reactive.enabled",
"description": "Enables ReactiveResilience4J auto-configuration.",
"type": "java.lang.Boolean"
},
{
"defaultValue": "true",
"name": "spring.cloud.circuitbreaker.bulkhead.resilience4j.enabled",
"description": "Enables to use of the Bulkhead by Resilience4j.",
"type": "java.lang.Boolean"
}
]
}
- spring.cloud.circuitbreaker.resilience4j.blocking.enabled 和 spring.cloud.circuitbreaker.resilience4j.enabled 作用相同
- spring.cloud.circuitbreaker.bulkhead.resilience4j.enabled 可以关闭 bulkhead
启用的 spring 的 circuitbreaker(circuitbreaker timelimiter bulkhead) 的默认配置(重要)
circuitbreaker
需要留意的配置
slowCallDurationThreshold : 默认60s 如果某些逻辑 本来就会超过60s 需要 按需更改 大于60s
ignoreExceptions : 默认[] 如果某些逻辑 抛出业务异常 (如businessException) 需要ignore
全部配置
配置属性 | 默认值 | 描述 |
---|---|---|
failureRateThreshold | 50 | 以百分比配置失败率阈值。当失败率等于或大于阈值时,断路器状态并关闭变为开启,并进行服务降级。 |
slowCallRateThreshold | 100 | 以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold 的调用视为满调用,当慢调用比例大于等于阈值时,断路器开启,并进行服务降级。 |
slowCallDurationThreshold | 60000 [ms] | 配置调用时间的阈值,高于该阈值的呼叫视为慢调用,并增加慢调用比例。 |
permittedNumberOfCallsInHalfOpenState | 10 | 断路器在半开状态下允许通过的调用次数。 |
maxWaitDurationInHalfOpenState | 0 | 断路器在半开状态下的最长等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会一直处于半开状态,直到所有允许通过的访问结束。 |
slidingWindowType | COUNT_BASED | 配置滑动窗口的类型,当断路器关闭时,将调用的结果记录在滑动窗口中。滑动窗口的类型可以是count-based或time-based。如果滑动窗口类型是COUNT_BASED,将会统计记录最近slidingWindowSize 次调用的结果。如果是TIME_BASED,将会统计记录最近slidingWindowSize 秒的调用结果。 |
slidingWindowSize | 100 | 配置滑动窗口的大小。 |
minimumNumberOfCalls | 100 | 断路器计算失败率或慢调用率之前所需的最小调用数(每个滑动窗口周期)。例如,如果minimumNumberOfCalls为10,则必须至少记录10个调用,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。 |
waitDurationInOpenState | 60000 [ms] | 断路器从开启过渡到半开应等待的时间。 |
automaticTransition FromOpenToHalfOpenEnabled | false | 如果设置为true,则意味着断路器将自动从开启状态过渡到半开状态,并且不需要调用来触发转换。创建一个线程来监视断路器的所有实例,以便在WaitDurationInOpenstate之后将它们转换为半开状态。但是,如果设置为false,则只有在发出调用时才会转换到半开,即使在waitDurationInOpenState之后也是如此。这里的优点是没有线程监视所有断路器的状态。 |
recordExceptions | empty | 记录为失败并因此增加失败率的异常列表。 除非通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。 如果指定异常列表,则所有其他异常均视为成功,除非它们被ignoreExceptions显式忽略。 |
ignoreExceptions | empty | 被忽略且既不算失败也不算成功的异常列表。 任何与列表之一匹配或继承的异常都不会被视为失败或成功,即使异常是recordExceptions的一部分。 |
recordException | throwable -> true· By default all exceptions are recored as failures. | 一个自定义断言,用于评估异常是否应记录为失败。 如果异常应计为失败,则断言必须返回true。如果出断言返回false,应算作成功,除非ignoreExceptions显式忽略异常。 |
ignoreException | throwable -> false By default no exception is ignored. | 自定义断言来判断一个异常是否应该被忽略,如果应忽略异常,则谓词必须返回true。 如果异常应算作失败,则断言必须返回false。 |
timelimiter
timeoutDuration : 默认 1s : 超时时间配置 (类型是 Duration),这个配置肯定要改,建议(最少)默认值改成: 10s
cancelRunningFuture : 默认 true: 当 future.get 超时时候(TimeoutException),是否调用 future.cancel 取消异步任务
bulkhead
BulkheadConfig
配置属性 | 默认值 | 描述 |
---|---|---|
maxConcurrentCalls | 25 | 隔离允许线程并发执行的最大数量 |
maxWaitDuration | 0 | 当达到并发调用数量时,新的线程执行时将被阻塞,这个属性表示最长的等待时间。 |
ThreadPoolBulkheadConfig
配置属性 | 默认值 | 描述 |
---|---|---|
maxThreadPoolSize | Runtime.getRuntime().availableProcessors() | Configures the max thread pool size. |
coreThreadPoolSize | Runtime.getRuntime().availableProcessors()- 1 | Configures the core thread pool size |
queueCapacity | 100 | Configures the core thread pool size |
keepAliveDuration | 20 [ms] | When the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. |
建议
我觉没必要 刻意默认禁用 bulkhead
spring Resilience4JCircuitBreakerFactory 线程池分析(重要)
Resilience4JCircuitBreakerFactory
成员属性
1.executorService
executorService = Executors.newCachedThreadPool();
说明:
构造
1. 支持自定义该变量
使用
1. create 方法中(不带 groupName 参数) 创建 Resilience4JCircuitBreaker 用到,传给 Resilience4JCircuitBreaker构造方法
2. ConcurrentHashMap<String, ExecutorService> executorServices
构造 和使用
1. 不支持自定义该变量
2. create 方法中(带 groupName 参数) groupExecutorService = executorServices.computeIfAbsent(groupName,
group -> Executors.newCachedThreadPool()) 然后传给 Resilience4JCircuitBreaker
Resilience4JCircuitBreaker
1.executorService
当 bulkhead 不存在时候 executorService 直接作为执行 业务方法的封装 然后传给 timeLimiter
当 bulkhead 存在的时候 ,则不会使用,使用 bulkhead 自己的线程池。
PS:
- Resilience4JCircuitBreaker 的 groupName 会作为 bulkhead 的id,
- groupName 如果没传 默认直接和 id相等 因为 Resilience4JCircuitBreakerFactory 有个不带groupName 的create方法。
与 resilience4j-spring-boot2 的关系
Spring Cloud Circuit Breaker 的实现 依赖与 resilience4j-spring-boot2 自动生成的 类(比如: CircuitBreakerRegistry, TimeLimiterRegistry, BulkheadRegistry)。
所以 我们可以直接使用 resilience4j-spring-boot2 的配置格式,优先级高于 spring 自带的 Customizer。但是低于 addCircuitBreakerCustomizer 中的 Customizer。
属性配置demo 如下
resilience4j.circuitbreaker:
instances:
backendA:
registerHealthIndicator: true
slidingWindowSize: 100
backendB:
registerHealthIndicator: true
slidingWindowSize: 10
permittedNumberOfCallsInHalfOpenState: 3
slidingWindowType: TIME_BASED
recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
resilience4j.timelimiter:
instances:
backendA:
timeoutDuration: 2s
cancelRunningFuture: true
backendB:
timeoutDuration: 1s
cancelRunningFuture: false
为什么 spring 需要增加类 Resilience4jBulkheadProvider
因为 Spring Cloud Circuit Breaker 强制依赖 resilience4j 的 (CircuitBreakerRegistry 和 TimeLimiterRegistry) 但是 把 Bulkhead 作为一个可选功能提供。
扩展
上文中 resilience4j 的 tags 作用是什么??
用作监控指标的tags