如何使用java 8功能简化重试代码块

Moo*_*lit 11 java java-8

在我的代码中,我有一个试图连接到某个外部接口的部分,如果失败,那么它将重试它固定的次数.代码有效,但有点难看.我想知道是否可以使用一些奇特的Java8功能以更优雅的方式完成此操作?

int count = 0;
final int maxRetries = 3;
while ( count < maxRetries )
{
   try
   {
     // Some Code
     // break out of loop on success
   }
   catch ( final ExecutionException e )
   {
      LOG.debug( "retrying..." );
      if ( ++count >= maxRetries )
      {
         LOG.debug( "do something else...");
         //do something else
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

Bri*_*etz 19

你可以做的是分离出重试逻辑.你需要一些辅助脚手架:

interface ThrowingTask {
    void run() throws ExecutionException;
}
Run Code Online (Sandbox Code Playgroud)

现在,你写道:

boolean runWithRetries(int maxRetries, ThrowingTask t) { 
    int count = 0;
    while (count < maxRetries) {
        try {
            t.run();
            return true;
        }
        catch (ExecutionException e) {
            if (++count >= maxRetries)
                return false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过重试来运行,而无需将任务逻辑与重试逻辑混淆:

runWithRetries(MAX_RETRIES, () -> { /* do stuff */ });
Run Code Online (Sandbox Code Playgroud)

你可以调整它,因为你喜欢接受在重试时调用的lambda,返回重试计数等等.但是游戏是编写runWithRetries捕获控制流的方法,但是抽象需要做什么行为 - 所以你只需要编写一次重试循环,然后在需要的地方填写所需的实际行为.


Jon*_*han 10

使用故障安全

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(ExecutionException.class)
  .withMaxRetries(3);

Failsafe.with(retryPolicy)
  .onRetry(r -> LOG.debug("retrying..."))
  .withFallback(e -> LOG.debug("do something else..."))
  .run(() -> someCode());
Run Code Online (Sandbox Code Playgroud)

就您的用例而言,它是如此简单和富有表现力。


tka*_*uko 6

好吧,在我看来,更实用的方法是使用Trymonad,不幸的是在jdk 8中不存在monad :(

尽管如此,您仍然可以使用提供它的更好的monads库。有了它,您可以提出一些类似的实现:

public static <Out> Try<Out> tryTimes(int times, TrySupplier<Out> attempt) {
        Supplier<Try<Out>> tryAttempt = () -> Try.ofFailable(attempt::get);

        return IntStream.range(1, times)
                .mapToObj(i -> tryAttempt)
                .reduce(tryAttempt, (acc, current) -> () -> acc.get().recoverWith(error -> current.get()))
                .get();
    }
Run Code Online (Sandbox Code Playgroud)

简而言之,此函数仅链接的调用,tryAttempt如果尝试失败,将尝试recoverWith调用的下一个调用tryAttempt。客户端代码如下所示:

tryTimes(10, () -> {
            // all the logic to do your possibly failing stuff
        }
);
Run Code Online (Sandbox Code Playgroud)

结果,将获得客户端代码Try<T>,可以通过直接调用.get()(如果成功返回值,如果失败则抛出基础异常)或使用库文档中描述的其他方法来解压缩客户端代码。

希望能帮助到你。

更新:

这也使用来完成功能性的方式filterfindFirst并且limit,没有任何外部库:

interface ThrowingSupplier<Out> { Out supply() throws Exception; }

public static <Out> Optional<Out> tryTimes(int times, ThrowingSupplier<Out> attempt) {
    Supplier<Optional<Out>> catchingSupplier = () -> {
        try {
            return Optional.ofNullable(attempt.supply());
        } catch (Exception e) {
            return Optional.empty();
        }
    };
    return Stream.iterate(catchingSupplier, i -> i)
            .limit(times)
            .map(Supplier::get)
            .filter(Optional::isPresent)
            .findFirst()
            .flatMap(Function.identity());
}
Run Code Online (Sandbox Code Playgroud)

客户端代码保持不变。另外,请注意,它不会评估表达times时间,但是会在第一次成功尝试后停止。