这篇文章将讨论如何在 Java 中实现重试逻辑。

1. 简单的 for 循环与 try-catch

在 Java 中实现重试逻辑的一个简单解决方案是将代码编写在执行指定次数(最大重试值)的 for 循环中。

以下程序对此进行了演示。请注意,代码包含在 try-catch 中,如果 try 块内发生异常,控件将转到 catch 块。处理异常后,系统会在 1 秒后再次运行代码。在所有重试都用尽并且最后一次重试失败后,系统会引发异常。



import java.util.Random;
 
public class Main {
    private static final int MAX_RETRIES = 5;
 
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i <= MAX_RETRIES; i++) {
            try {
                // generate 0 or 1 with equal probability
                int zeroOrOne = new Random().nextInt(2);
 
                System.out.println("Random number is.. " + zeroOrOne);
 
                // 50% probability of getting java.lang.ArithmeticException: / by zero
                int rand = 1 / zeroOrOne;
 
                // don't retry on success
                break;
            } catch (Exception ex) {
                // handle exception
                System.out.println(ex.getMessage());    // log the exception
 
                // sleep for 1 seconds before retrying (Optional)
                Thread.sleep(1000);
 
                // throw exception if the last re-try fails
                if (i == MAX_RETRIES) {
                    throw ex;
                }
            }
        }
    }
}


输出(会有所不同):
随机数是.. 0 / 乘以零 随机数为.. 0

/ 乘以零

随机数为.. 1
 

2. 使用界面

我们可以轻松地调整上述逻辑,以使用接口将任务逻辑与重试逻辑隔离开来。以下代码对此进行了演示。



import java.util.Random;
 
interface Task {
    void run();
    void handleException(Exception ex);
}
 
public class Main {
    private static final int MAX_RETRIES = 5;
 
    public static void withMaxRetries(Task task) {
        for (int i = 0; i <= MAX_RETRIES; i++) {
            try {
                task.run();
                break;                                    // don't retry on success
            } catch (Exception ex) {
                task.handleException(ex);
 
                // throw exception if the last re-try fails
                if (i == MAX_RETRIES) {
                    throw ex;
                }
            }
        }
    }
 
    public static void main(String[] args) {
        withMaxRetries(new Task() {
            @Override
            public void run() {
                // generate 0 or 1 with equal probability
                int zeroOrOne = new Random().nextInt(2);
 
                System.out.println("Random number is.. " + zeroOrOne);
 
                // 50% probability of getting java.lang.ArithmeticException: / by zero
                int rand = 1 / zeroOrOne;
            }
 
            @Override
            public void handleException(Exception ex) {
                System.out.println(ex.getMessage());    // log the exception
 
                try {
                    // sleep for 1 seconds before retrying (Optional)
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println(e.getMessage());    // log the exception
                }
            }
        });
    }
}


输出(会有所不同):
随机数是.. 0 / 乘以零 随机数为.. 0 / 乘以零 随机数为.. 0

/ 乘以零


随机数为.. 1

 

3. 第三方库

如果您的项目准备使用第三方库,我们建议您使用以下库,这些库在 Java 中对重试逻辑有很强的支持。

1. 故障安全

故障保护是一个轻量级的零依赖库,用于处理 Java 8+ 中的故障。故障保护的工作原理是将可执行逻辑与一个或多个弹性策略包装在一起,这些策略可以根据需要组合和组合。

首先,创建一个重试策略,该策略定义应处理哪些故障以及何时应执行重试:



RetryPolicy<Object> retryPolicy = RetryPolicy.builder()
    .handle(ConnectException.class)
    .withDelay(Duration.ofSeconds(1))
    .withMaxRetries(3)
    .build();


 
然后,您可以重试执行 或:RunnableSupplier



// Run with retries
Failsafe.with(retryPolicy).run(() -> connect());
 
// Get with retries
Connection connection = Failsafe.with(retryPolicy).get(() -> connect());


 

2. 番石榴重试

guava 重试模块提供了一种通用方法,用于重试任意 Java 代码,具有特定的停止、重试和异常处理功能,这些功能通过 Guava 的谓词匹配得到增强。

某些功能的最小示例如下所示:



Callable<Boolean> callable = new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return true; // do something useful here
    }
};
 
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfResult(Predicates.<Boolean>isNull())
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .build();
 
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}


  每当 的结果为 null、引发 或从方法中抛出任何其他结果时,这将重试。它将在尝试重试 3 次后停止,并抛出包含有关上次失败尝试的信息的 。如果从方法中弹出任何其他内容,则会将其包装并重新抛出。