resilience4j + Spring 断路器实例
resilience4j + Spring instance of CircuitBreaker
我想使用Resilience4j来处理容错,我正在使用CircuitBreaker和TimerLimit。
我想分离容错行为的业务逻辑,而不是"dirty"我的业务代码。
所以,我正在考虑使用命令模式来执行我将要处理的方法,就像 Hystrix 对 HystrixCommand 所做的那样。
示例:
public class MyCommand {
private static final CircuitBreaker circuitBreaker;
private Long param1, param2;
private MyService myService;
private static final TimeLimiter timeLimiter;
static {
long ttl = 50000;
TimeLimiterConfig configTimerLimit
= TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(ttl)).build();
timeLimiter = TimeLimiter.of(configTimerLimit);
// I got the configuration from a class that I created.
circuitBreaker = CircuitBreaker.of("my", CircuitBreakerConfigOptions.defaultForExternalService());
}
public MyCommand(Long param1, Long param2, MyService myService) {
this.param1 = param1;
this.param2 = param2;
this.myService = myService;
}
public String run() {
Callable<String> stringCallable = TimeLimiter.decorateFutureSupplier(timeLimiter,
() -> CompletableFuture.supplyAsync(() -> myService.hello(param1, param2)));
Callable<String> callable = CircuitBreaker.decorateCallable(circuitBreaker, stringCallable);
return Try.of(callable::call).recover(t -> fallback(t)).get();
}
protected String fallback(Throwable throwable) {
Callable<String> stringCallable = TimeLimiter.decorateFutureSupplier(timeLimiter,
() -> CompletableFuture.supplyAsync(() -> myService.otherHello(param1, param2)));
return Try.of(stringCallable::call).getOrElse("Fallback");
}
}
调用我的控制器:
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
return new MyCommand(1L, 2L, new MyService()).run();
}
我的疑惑:
1 - 在这种情况下,circuitBreaker 确实需要是静态的,因为我知道同一个对象需要在您想要威胁的同一个方法之间共享,我说得对吗?
2 - 我如何拥有此应用程序的多个实例,断路器对每个实例单独工作?我说的对吗?
因为您似乎正在使用 Spring 启动,您可以使用 resilience4j-spring-boot-2 启动模块,它也支持注释。
我从你的问题中了解到 - 你需要一个 Resilience4j 的断路器,它应该是独立的,即不与你的业务逻辑混淆。
所以我建议在 运行() 方法周围放置断路器保护。下面的代码将详细说明 -
您的控制器 -
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
return new MyCommand().run();
}
现在用@CircuitBreaker
编写 MyCommand class 的 运行() 方法
public class MyCommand {
@CircuitBreaker(name = "RUN_METHOD_PROTECTION") // <---- here is our circuit breaker annotation code top of below your business code... and that’s it.
Your_Response run(Your_Request){
// Your business logic written here...
}
在您的 YAML 属性 文件中进一步添加断路器配置,如下所示(我使用的是基于计数而不是基于时间)–
resilience4j.circuitbreaker:
backends:
RUN_METHOD_PROTECTION:
registerHealthIndicator: true
slidingWindowSize: 100 # start rate calc after 100 calls
minimumNumberOfCalls: 100 # minimum calls before the CircuitBreaker can calculate the error rate.
permittedNumberOfCallsInHalfOpenState: 10 # number of permitted calls when the CircuitBreaker is half open
waitDurationInOpenState: 10s # time that the CircuitBreaker should wait before transitioning from open to half-open
failureRateThreshold: 50 # failure rate threshold in percentage
slowCallRateThreshold: 100 # consider all transactions under interceptor for slow call rate
slowCallDurationThreshold: 2s # if a call is taking more than 2s then increase the error rate
recordExceptions: # increment error rate if following exception occurs
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
- org.springframework.web.client.ResourceAccessException
现在,如果您无法在您的项目中使用注解@CircuitBreaker,那么您也可以以功能方式进行操作,即
假设我们在您的配置中定义了一个 bean,
@Bean
public CircuitBreaker MyCircuitBreaker(){
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.slidingWindow(100,100, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.failureRateThreshold(50)
.build();
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker circuitBreaker = registry.circuitBreaker("your_run_method_CircuitBreker"); // here you are registering your circuit breaker with a unique tag. And in future you refer this tag you get a same circuit breaker.
return circuitBreaker;
}
现在你的控制器代码会在下面 -
private CircuitBreaker circuitBreaker; // Assume you have injected the value from CircuitBreaker bean
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
Function<Your_Request, Your_Response> decorated = CircuitBreaker
.decorateFunction(circuitBreaker, new MyCommand().run());
return decorated.apply();
}
这样你也不会干扰你的业务逻辑。
我想使用Resilience4j来处理容错,我正在使用CircuitBreaker和TimerLimit。
我想分离容错行为的业务逻辑,而不是"dirty"我的业务代码。
所以,我正在考虑使用命令模式来执行我将要处理的方法,就像 Hystrix 对 HystrixCommand 所做的那样。
示例:
public class MyCommand {
private static final CircuitBreaker circuitBreaker;
private Long param1, param2;
private MyService myService;
private static final TimeLimiter timeLimiter;
static {
long ttl = 50000;
TimeLimiterConfig configTimerLimit
= TimeLimiterConfig.custom().timeoutDuration(Duration.ofMillis(ttl)).build();
timeLimiter = TimeLimiter.of(configTimerLimit);
// I got the configuration from a class that I created.
circuitBreaker = CircuitBreaker.of("my", CircuitBreakerConfigOptions.defaultForExternalService());
}
public MyCommand(Long param1, Long param2, MyService myService) {
this.param1 = param1;
this.param2 = param2;
this.myService = myService;
}
public String run() {
Callable<String> stringCallable = TimeLimiter.decorateFutureSupplier(timeLimiter,
() -> CompletableFuture.supplyAsync(() -> myService.hello(param1, param2)));
Callable<String> callable = CircuitBreaker.decorateCallable(circuitBreaker, stringCallable);
return Try.of(callable::call).recover(t -> fallback(t)).get();
}
protected String fallback(Throwable throwable) {
Callable<String> stringCallable = TimeLimiter.decorateFutureSupplier(timeLimiter,
() -> CompletableFuture.supplyAsync(() -> myService.otherHello(param1, param2)));
return Try.of(stringCallable::call).getOrElse("Fallback");
}
}
调用我的控制器:
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
return new MyCommand(1L, 2L, new MyService()).run();
}
我的疑惑:
1 - 在这种情况下,circuitBreaker 确实需要是静态的,因为我知道同一个对象需要在您想要威胁的同一个方法之间共享,我说得对吗?
2 - 我如何拥有此应用程序的多个实例,断路器对每个实例单独工作?我说的对吗?
因为您似乎正在使用 Spring 启动,您可以使用 resilience4j-spring-boot-2 启动模块,它也支持注释。
我从你的问题中了解到 - 你需要一个 Resilience4j 的断路器,它应该是独立的,即不与你的业务逻辑混淆。
所以我建议在 运行() 方法周围放置断路器保护。下面的代码将详细说明 -
您的控制器 -
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
return new MyCommand().run();
}
现在用@CircuitBreaker
编写 MyCommand class 的 运行() 方法public class MyCommand {
@CircuitBreaker(name = "RUN_METHOD_PROTECTION") // <---- here is our circuit breaker annotation code top of below your business code... and that’s it.
Your_Response run(Your_Request){
// Your business logic written here...
}
在您的 YAML 属性 文件中进一步添加断路器配置,如下所示(我使用的是基于计数而不是基于时间)–
resilience4j.circuitbreaker:
backends:
RUN_METHOD_PROTECTION:
registerHealthIndicator: true
slidingWindowSize: 100 # start rate calc after 100 calls
minimumNumberOfCalls: 100 # minimum calls before the CircuitBreaker can calculate the error rate.
permittedNumberOfCallsInHalfOpenState: 10 # number of permitted calls when the CircuitBreaker is half open
waitDurationInOpenState: 10s # time that the CircuitBreaker should wait before transitioning from open to half-open
failureRateThreshold: 50 # failure rate threshold in percentage
slowCallRateThreshold: 100 # consider all transactions under interceptor for slow call rate
slowCallDurationThreshold: 2s # if a call is taking more than 2s then increase the error rate
recordExceptions: # increment error rate if following exception occurs
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
- org.springframework.web.client.ResourceAccessException
现在,如果您无法在您的项目中使用注解@CircuitBreaker,那么您也可以以功能方式进行操作,即
假设我们在您的配置中定义了一个 bean,
@Bean
public CircuitBreaker MyCircuitBreaker(){
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.slidingWindow(100,100, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.failureRateThreshold(50)
.build();
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker circuitBreaker = registry.circuitBreaker("your_run_method_CircuitBreker"); // here you are registering your circuit breaker with a unique tag. And in future you refer this tag you get a same circuit breaker.
return circuitBreaker;
}
现在你的控制器代码会在下面 -
private CircuitBreaker circuitBreaker; // Assume you have injected the value from CircuitBreaker bean
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
Function<Your_Request, Your_Response> decorated = CircuitBreaker
.decorateFunction(circuitBreaker, new MyCommand().run());
return decorated.apply();
}
这样你也不会干扰你的业务逻辑。