resilience4j + CircuitBreaker的Spring实例

时间:2019-04-23 16:54:34

标签: spring resilience4j

我想使用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可以分别为每个实例工作?我是对的吗?

2 个答案:

答案 0 :(得分:2)

我从您的问题中了解的是-您需要一个Resilience4j的断路器,该断路器应该是独立的,即不会弄乱您的业务逻辑。

因此,我建议将断路器保护置于run()方法周围。下面的代码将详细说明-

您的控制器-

@ApiOperation(value = "Only to test")
@GetMapping(value = "/execute", produces = MediaType.APPLICATION_JSON)
public String execute() {
    return new MyCommand().run();  
}

现在使用@CircuitBreaker编写MyCommand类的run()方法

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();
}

通过这种方式,您也不会干扰业务逻辑。

答案 1 :(得分:0)

由于您似乎正在使用Spring Boot,因此可以使用resilience4j-spring-boot-2入门模块,该模块也支持注释。

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