0

我想使用 Resilience4j 来处理容错,我正在使用 CircuitBreaker 和 TimerLimit。

我想分离容错行为的业务逻辑,以免“弄脏”我的业务代码。

所以,我正在考虑使用命令模式来执行我的方法,就像 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 - 我如何有这个应用程序的许多实例,断路器为每个实例单独工作?我是对的?

4

2 回答 2

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

这种方式也不会干扰您的业务逻辑。

于 2020-01-07T07:43:01.183 回答
0

由于您似乎正在使用 Spring Boot,因此您可以使用还支持注释的弹性 4j-spring-boot-2 启动模块。

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

于 2019-06-21T06:21:33.170 回答