resilience4j + Spring экземпляр 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 - В этом случае circuitBreaker действительно должен быть статическим, потому что я понял, что один и тот же объект должен использоваться одним и тем же методом, которым вы хотите угрожать, я прав?

2 - Сколько у меня экземпляров этого приложения, circuitBreaker работает индивидуально для каждого экземпляра? Я не ошибаюсь?


person javaTry    schedule 23.04.2019    source источник


Ответы (2)


Поскольку кажется, что вы используете Spring Boot, вы можете использовать стартовый модуль resilience4j-spring-boot-2, который также поддерживает аннотации.

https://resilience4j.readme.io/docs/getting-started-3

person Robert Winkler    schedule 21.06.2019

Что я понимаю из вашего вопроса: вам нужен автоматический выключатель 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();
}

Таким образом, вы также никогда не вмешивались в свою бизнес-логику.

person atul sachan    schedule 07.01.2020