resilience4j + весенний экземпляр CircuitBreaker
Я хотел бы использовать Resilience4j для борьбы с отказоустойчивостью, я использую CircuitBreaker и TimerLimit.
Я бы хотел отделить бизнес-логику от отказоустойчивого поведения, чтобы не "пачкать" мой бизнес-код.
Итак, я думаю использовать шаблон Command для выполнения моих методов, которые будут обрабатываться, как 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 - В этом случае CircleBreaker действительно должен быть статичным, потому что я понял, что один и тот же объект должен быть разделен между тем же методом, который вы хотите угрожать, я прав?
2 - Как много у меня экземпляров этого приложения, CircleBreaker работает индивидуально для каждого экземпляра? Я не ошибаюсь?
0 ответов
Что я понимаю из вашего вопроса: вам нужен автоматический выключатель Resilience4j, который должен быть Sperate, т.е. не нарушать вашу бизнес-логику.
Поэтому я бы предложил поставить защиту автоматического выключателя вокруг метода run(). ниже код будет уточнять -
Ваш контроллер -
@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
return new MyCommand().run();
}
Теперь напишите свой метод run() класса MyCommand с помощью @CircuitBreaker
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();
}
Таким образом, вы также никогда не вмешивались в свою бизнес-логику.
Поскольку кажется, что вы используете Spring Boot, вы можете использовать стартовый модуль resilience4j-spring-boot-2, который также поддерживает аннотации.