【Spring Cloud】 CircuitBreaker 组件 resilience4j 二
前言
上章节了解了 resilience4j
的单独使用方法,同样它也可以完美契合 Spring(SpringBoot ...)
,对此 resilience4j
官方提供了 resilience4j-spring
和 resilience4j-spring-boot2
两个 module
当然还有 resilience4j-spring-cloud,本文略
SpringBoot 下的使用
resilience4j-spring
如图,resilience4j-spring
模块主要提供这些功能(以 CircuitBreaker 组件示例,其他基本雷同)
Aspect
切面逻辑,首先我们肯定不想手工coding
去装饰一个个客户端请求,因此注解形式的声明可以大大降低工作量且更加Spring
,其中的注解、逻辑相关都有此类提供,熔断组件的声明注解即@CircuitBreaker
- 核心配置类,提供核心组件类诸如
CircuitBreakerRegistry
等的装配 ConfigurationProperties
的提供,这是我们通过配置文件进行组件属性配置的核心,根据此配置Spring
就可以帮我们创建相应的组件
可以看到 resilience4j-spring
基本提供了所有我们想要的功能
resilience4j-spring-boot2
到了 SpringBoot
要干的活就不多了,它提供了多组自动装配类,使得我们可以 “零” 配置去使用 resilience4j
组件,直接看示例,同时基于注解的声明要用到 Spring AOP
,因此需要引入对应依赖 spring-boot-starter-aop
application.yaml
# bulkhead(基于信号量)
resilience4j.bulkhead:
instances:
delay:
maxConcurrentCalls: 1
# ThreadPoolBulkhead
resilience4j.thread-pool-bulkhead:
instances:
delay:
maxThreadPoolSize: 1
coreThreadPoolSize: 1
queueCapacity: 1
# TimeLimter
resilience4j.timelimiter:
instances:
delay:
timeoutDuration: 1s
# RateLimiter
resilience4j.ratelimiter:
instances:
delay:
timeoutDuration: 0
limitRefreshPeriod: 3s
limitForPeriod: 2
# CircuitBreaker
resilience4j.circuitbreaker:
instances:
delay:
failureRateThreshold: 50
slowCallRateThreshold: 100
slowCallDurationThreshold: 500ms
minimumNumberOfCalls: 2
waitDurationInOpenState: 60s
recordExceptions:
- com.xsn.circuitbreaker.resilience4j.exception.MyException
# Retry
resilience4j.retry:
instances:
delay:
maxAttempts: 2
waitDuration: 500ms
retryExceptions:
- com.xsn.circuitbreaker.resilience4j.exception.MyException
failAfterMaxRetries: true
配置项不难理解,如上我们声明了一组 name: delay
的组件
值得一提的是,切面是有优先级的,默认:Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( Function ) ) ) ) )
,当然也可以通过属性修改优先级如下(数字越大优先级越高):
resilience4j:
circuitbreaker:
circuitBreakerAspectOrder: 1
retry:
retryAspectOrder: 2
Decorate Function
@Bulkhead(name = "delay")
@RateLimiter(name = "delay", fallbackMethod = "rateLimiterFallback")
@CircuitBreaker(name = "delay")
@Retry(name = "delay")
@RequestMapping(value = "/delay", method = RequestMethod.GET)
public String delay(@RequestParam("time") int time) {
System.out.println("invoke ...");
return restTemplate.getForObject(
"http://EUREKA-CLIENT-1/delay?time=" + time
, String.class
);
}
public String rateLimiterFallback(int time, Throwable e) {
return MessageFormat.format(
"rate out, param: {0}, e: {1}"
, time, e.getMessage()
);
}
直接基于注解声明,比之前手工编码方便多了,其中 rateLimiterFallback
作为 RateLimiter
组件的回调,其参数(类型)要保持与目标方法完全相同,且多出的 Throwable
可以用来处理异常逻辑
同时,如果要使用 ThreadPoolBulkhead
和 TimeLimiter
组件,返回类型必须为 CompletionStage
或 Future
,如下:
@Bulkhead(name = "delay", type = Bulkhead.Type.THREADPOOL)
@RequestMapping(value = "/delayThread", method = RequestMethod.GET)
public CompletableFuture<String> delayThread(@RequestParam("time") int time) {
System.out.println("invoke ...");
Supplier<String> supplier = () -> restTemplate.getForObject(
"http://EUREKA-CLIENT-1/delay?time=" + time
, String.class
);
return CompletableFuture.supplyAsync(supplier);
}
小结
以上可以理解为 resilience4j
在 SpringBoot
中的使用,当然核心功能基本由 resilience4j-spring
提供,相对于编码方式这种使用就很轻松了
接下来我想聊一下 spring-cloud-circuitBreaker-resilience4j
SpringCloud 下的使用
spring-cloud-circuitBreaker-resilience4j
是这样一个项目:基于 resilience4j
实现 SpringCloud
的 熔断
组件功能,它提供了一个基于 spring-cloud-commons
层 熔断
抽象 CircuitBreakerFactory
的实现 Resilience4JCircuitBreakerFactory
同时,它的依赖中也是包含 resilience4j-spring-boot2
的
resilience4j-spring-boot2 由 resilience4j 官方实现
spring-cloud-circuitbreaker-resilience4j 由 SpringCloud 官方实现
,同时还提供有其他实现诸如:spring-cloud-circuitbreaker-spring-retry 等
因此,在 SpringCloud
虽然也是可以基于注解、配置文件去声明、使用所有 resilience4j
组件(使用注解也是需要 aop
的),但本质其实与 spring-cloud-circuitbreaker-resilience4j
无关,真正与 SpringCloud
有关的是 Resilience4JCircuiBreakerFactory
Resilience4JCircuiBreaker
等实例的使用,后者仅侧重于 resilience4j
的 CircuitBreaker
TimeLimter
Bulkhead(ThreadPoolBulkhead)
组件使用
相关的实现细节还是十分巧妙的,不多赘述,直接上示例
示例
@RestController
public class SCStyleController {
@Configuration
static class Config {
/**
* 通过 Resilience4JCircuitBreakerFactory 可以定制
* CircuitBreaker TimeLimiter 组件
* @return
*/
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> circuitBreakerFactoryCustomizer1() {
return factory -> factory.configureDefault(
id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(
CircuitBreakerConfig
.custom()
.slowCallDurationThreshold(Duration.ofSeconds(1))
.slowCallRateThreshold(50)
.minimumNumberOfCalls(4)
.build()
)
.timeLimiterConfig(
TimeLimiterConfig
.custom()
.timeoutDuration(Duration.ofSeconds(3))
.build()
)
.build()
);
}
/**
* 通过 Resilience4jBulkheadProvider 可以定制 Bulkhead
* ThreadPoolBulkhead 组件
* @return
*/
@Bean
public Customizer<Resilience4jBulkheadProvider> resilience4jBulkheadProviderCustomizer1() {
return provider -> provider.configure(
builder -> builder
.threadPoolBulkheadConfig(
ThreadPoolBulkheadConfig
.custom()
.maxThreadPoolSize(4)
.coreThreadPoolSize(2)
.queueCapacity(2)
.build()
)
, "delay"
);
}
}
@Autowired
CircuitBreakerFactory circuitBreakerFactory;
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "scDelay", method = RequestMethod.GET)
public String scDelay(@RequestParam(value = "time") int time) {
/**
* 通过 circuitBreakerFactory 创建 Resilience4JCircuitBreaker
* 实例
*/
return circuitBreakerFactory
.create("delay")
.run(() -> restTemplate.getForObject(
"http://EUREKA-CLIENT-1/delay?time=" + time
, String.class
));
}
}
这里仅仅使用了 spring-cloud-circuitbreaker
的抽象,即 Resilience4JCircuitBreakerFactory
实例,但实际上还是没有 resilience4j-spring
好用的,因此引入 spring-cloud-starter-circuitbreaker-resilience4j
依赖,同时以注解、配置文件的方式使用 resilience4j
这种 “挂羊头卖狗肉” 的行为,我认为也是可以接受的
总结
本章节主要阐述 resilience4j
在 Spring
环境下的使用,同时说明 SpringCloud
下的 resilience4j
整合本质是借助 resilience4j
去实现 SpringCloud
的 熔断
组件能力而非是对 resilience4j
的整合,因此在选择依赖的时候要有清晰的认识~