【问题标题】:Failsafe with RetryPolicy and CircuitBreaker throws CircuitBreakerOpenException使用 RetryPolicy 和 CircuitBreaker 进行故障保护会引发 CircuitBreakerOpenException
【发布时间】:2018-06-10 01:03:42
【问题描述】:

我正在尝试将重试策略与具有 Failsafe 的 CircuitBreaker 模式相结合,但是当尝试在电路打开并被中断的情况下出现 CircuitBreakerOpenException 异常。

https://github.com/jhalterman/failsafe

问题是通过将重试延迟设置为小于电路的关闭时间而产生的。

如何控制此异常,使重试策略不中断?我想这样做是因为我可以让多个实例同时向休息服务发起请求,并且重试不会中断。

我的代码:

public class UnstableApplication {
    private final int MAX_FAILS = 4;
    private AtomicInteger failCount = new AtomicInteger(1);

    public String generateId() throws Exception {
        if (failCount.getAndIncrement() < MAX_FAILS) {
            System.err.printf("UnstableApplication throws SampleException at '%s'\n", ZonedDateTime.now());
            throw new Exception();
        }

        final String id = UUID.randomUUID().toString();
        System.out.printf("UnstableApplication: id '%s' generated at '%s'\n", id, ZonedDateTime.now());

        return id;
    }

}

public class FailsafeExample {

    public static void main(String[] args) throws Exception {

        UnstableApplication app = new UnstableApplication();

        RetryPolicy retryPolicy = new RetryPolicy()
                .retryOn(Exception.class)
                .withDelay(2, TimeUnit.SECONDS)
                .withMaxRetries(5);

        CircuitBreaker breaker = new CircuitBreaker();
        breaker.withFailureThreshold(2);
        breaker.withDelay(5, TimeUnit.SECONDS);
        breaker.withSuccessThreshold(3);
        breaker.onOpen(() -> {
            System.out.println("Circuit breaker is open");
        });

        breaker.onClose(() -> {
            System.out.println("Circuit breaker is close");
        });

        breaker.onHalfOpen(() -> {
            System.out.println("Circuit breaker is half-close");
        }); 

        Failsafe.with(retryPolicy)
        .with(breaker)
        .onFailedAttempt((a, b) -> {
            System.out.println(
                    String.format("Failed with exception: %s, at %s, circuit-breaker state is: %s", 
                            b, ZonedDateTime.now(), breaker.getState()));
        })
        .onSuccess(cxn -> {
            System.out.println("Succcess!");
        })
        .onFailure(cxn -> {
            System.out.println("Failed!");
        })
        .get(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return app.generateId();
            }
        });
    }
}

我的结果:

UnstableApplication throws SampleException at '2019-05-31T16:30:09.214Z[Etc/UTC]'
Failed with exception: java.lang.Exception, at 2019-05-31T16:30:09.221Z[Etc/UTC], circuit-breaker state is: CLOSED
UnstableApplication throws SampleException at '2019-05-31T16:30:11.229Z[Etc/UTC]'
Circuit breaker is open
Failed with exception: java.lang.Exception, at 2019-05-31T16:30:11.230Z[Etc/UTC], circuit-breaker state is: OPEN
Exception in thread "main" net.jodah.failsafe.CircuitBreakerOpenException
    at net.jodah.failsafe.SyncFailsafe.call(SyncFailsafe.java:136)
    at net.jodah.failsafe.SyncFailsafe.get(SyncFailsafe.java:56)
    at com.kash.test.Foo.main(Foo.java:63)

【问题讨论】:

    标签: java circuit-breaker retrypolicy java-failsafe


    【解决方案1】:

    这是我在本例中尝试将断路器与重试策略结合使用时的理解:

    CircuitBreaker breaker = new CircuitBreaker()
                    .withFailureThreshold(3, 10)
                    .withSuccessThreshold(5)
                    .failOn(Exception.class)
                    .withDelay(30, TimeUnit.SECONDS);
    
    RetryPolicy retryPolicy = new RetryPolicy()
                    .retryOn(ConnectException.class)
                    .withDelay(3, TimeUnit.SECONDS)
                    .withMaxRetries(5);
    
    • 首先,它会重试 5 次,但由于 Circuit Breaker 的阈值是 3,因此在第 3 次“连续”重试尝试时,它将断开并打开电路。
    • 因此,一旦打开,它可能“仍可能重试”,但由于电路已经打开,仍在时间延迟下重试,将导致 CircuitBreakerOpenException。

    【讨论】:

      【解决方案2】:

      这是预期的行为。当电路打开时,对那个端点的请求就会停止——如果依赖关系被破坏或不可靠,当然你的重试也必须被切断,否则你会像没有一样容易地经历一堆重试。根本没有cb。

      如果您想继续发送请求,要么您的断路触发器过于敏感,要么您处于不需要断路的情况。故障安全强制执行的行为很可能是正确的方法。

      如果您真的想要这样做,请将重试逻辑放在发送给断路器的函数中,而不是使用故障安全中的 RetryPolicy。或者在重试策略中使用指数退避函数。

      【讨论】:

        猜你喜欢
        • 2021-09-23
        • 2011-06-26
        • 2018-02-18
        • 1970-01-01
        • 2014-08-08
        • 2015-10-01
        • 2012-05-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多