如何捕获 Guava 重试器内部代码抛出的异常?

How do I catch the exception thrown by the code inside a Guava retryer?

我有一个围绕某些代码的 Guava 重试器:

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
    .retryIfExceptionOfType(Exception.class)
    .withStopStrategy(MoreStopStrategies.liveStopAfterAttempt(retries))
    .withWaitStrategy(MoreWaitStrategies.liveExponentialWait(TimeUnit.MILLISECONDS, retrySleep, TimeUnit.MILLISECONDS))
    .build();

try {
  retryer.call(() -> {
    return doStuff();
  });
} catch (ExecutionException | RetryException e) {
  throw Throwables.propagate(e);
}

假设 doStuff() 抛出一个 ArithmeticException。我如何在 retryer.call() 之外捕获它?

因此重试器将尝试几次,失败并进入 catch (ExecutionException | RetryException e) 块。我将如何检索其中的 ArithmeticException

谢谢!

这是一个有点错误的模式。你说任何异常都可以重试。然后 ArithmeticException 就可以重试了。这不是你想要的。

这就是重试的实施方式。注意第二行的注释。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
//  .retryIfExceptionOfType(Exception.class)
    .withStopStrategy(MoreStopStrategies.liveStopAfterAttempt(retries))
    .withWaitStrategy(MoreWaitStrategies.liveExponentialWait(TimeUnit.MILLISECONDS, retrySleep, TimeUnit.MILLISECONDS))
    .build();

void doStuff() {
  try {
    retryer.call(() -> {
        doRealStuff();
      });
  } catch (RetryException e) {
    throw new RuntimeException("Call never succeeded", e);
  } catch (ExecutionException e) {
    Throwables.propagateIfPossible(e.getCause(), ArithmeticException.class);
    throw new RuntimeException("Unexpected", e);
  }
}

然后当你实际调用 doStuff:

try {
  doStuff();
} catch(ArithmeticException e) {
  System.err.println("Arithmetic exception caught:");
  e.printStackTrace(System.err);
}

没有看到您的 doStuff() 或至少不知道它抛出的异常类型很难回答。

一般来说,如果您 Retryer 考虑每个 Exception 不成功,它会重试并最终停止 - 但它会捕获 any Throwable 抛入执行的代码中(参见 Retryer code,第 162 行)

解决方案是:

  • 如果您的 doStuff() 仅在那些导致重试器停止的情况下抛出 ArithmeticException,并且另一种异常表明需要重试,您可以 retryIfExceptoinOfType(OtherException.class).然后,根据文档 the docs

Throws:

java.util.concurrent.ExecutionException - if the given callable throws an exception, and the rejection predicate considers the attempt as successful. The original exception is wrapped into an ExecutionException.

call() 将抛出一个 ExecutionException,您可以在 Retryer 外部捕获它并检查包装的异常。

  • 如果 ArithmeticExcpetion 是全部 doStuff() 将抛出,并且这也表明 Retryer 应该重试,那么您可以编写一个自定义 StopStrategy 来抛出 ArithmeticExpcetion , 它在重试器中无处可寻。

但是我建议您完全重写您的代码,您的数据流似乎有点损坏。如果 ArithmeticExpcetion (或者实际上什至是 any 异常,正如您在 retryIf 中指定的 Exception.class )是预期的,它不应该 also 出乎意料,需要特殊处理。