春天重试,因为冬天来了

好的,这实际上与冬天无关,众所周知,冬天已经到了 。 它是关于Spring Retry的,Spring是一个小的Spring框架库,它使我们可以将重试功能添加到任何应重试的任务中。

这里有一个很好的教程 ,解释了如何设置简单的重试和恢复。 它很好地解释了如何添加spring-retry依赖项 ,使用@Retryable@Recover批注以及将RetryTemplate与简单策略一起使用。 当我们实际上想根据异常的类型应用不同的重试行为时,我想讲的是一个稍微复杂的情况。 这是有道理的,因为我们可能知道有些异常是可恢复的,而有些则是不可恢复的,因此尝试从异常中恢复并没有太大的意义。 为此,有一个特定的重试策略实现,称为ExceptionClassifierRetryPolicy ,与Spring RetryTemplate一起使用

假设我们只能从IO异常中恢复并跳过所有其他异常 。 我们将创建三个类来扩展RetryCallback,并创建一个类来扩展RecoveryCallback以更好地显示内部发生的情况:

private class SuccessCallback implements RetryCallback<Boolean, RuntimeException> {
        @Override
        public Boolean doWithRetry(RetryContext context) throws RuntimeException {
            System.out.println("Success callback: attempt " + context.getRetryCount());
            return true;
        }
    }

    private class ExceptionCallback implements RetryCallback<Boolean, Exception> {
        @Override
        public Boolean doWithRetry(RetryContext context) throws Exception {
            System.out.println("Exception callback: attempt " + context.getRetryCount());
            throw new Exception("Test Exception");
        }
    }

    private class SpecificExceptionCallback implements RetryCallback<Boolean, IOException> {
        @Override
        public Boolean doWithRetry(RetryContext context) throws IOException {
            System.out.println("IO Exception callback: attempt " + context.getRetryCount());
            throw new IOException("Test IO Exception");
        }
    }

    private class LoggingRecoveryCallback implements RecoveryCallback<Boolean> {
        @Override
        public Boolean recover(RetryContext context) throws Exception {
            System.out.println("Attempts exhausted. Total: " + context.getRetryCount());
            System.out.println("Last exception: " + Optional.ofNullable(context.getLastThrowable())
                    .orElse(new Throwable("No exception thrown")).getMessage());
            System.out.println("\n");
            return false;
        }
    }

然后,我们设置RetryTemplate 。 我们将使用SimpeRetryPolicy和IOException的固定尝试次数,以及一个NeverRetryPolicy ,它仅允许对其他所有事物进行初始尝试。

*
            We want to retry on IOException only.
            Other Exceptions won't be retried.
            IOException will be retried three times, counting the initial attempt.
         */
        final ExceptionClassifierRetryPolicy exRetryPolicy = new ExceptionClassifierRetryPolicy();
        exRetryPolicy.setPolicyMap(new HashMap<Class<? extends Throwable>, RetryPolicy>() {{
            put(IOException.class, new SimpleRetryPolicy(3));
            put(Exception.class, new NeverRetryPolicy());
        }});
        retryTemplate.setRetryPolicy(exRetryPolicy);

现在,我们需要使用这些回调来演示它们如何工作。 首先成功执行,这很简单:

// we do not catch anything here
        System.out.println("\n*** Executing successfull callback...");
        retryTemplate.execute(new SuccessCallback(), new LoggingRecoveryCallback());

其输出如下:

*** Executing successfull callback...
Success callback: attempt 0

然后是异常

// we catch Exception to allow the program to continue
        System.out.println("\n*** Executing Exception callback...");
        try {
            retryTemplate.execute(new ExceptionCallback(), new LoggingRecoveryCallback());
        } catch (Exception e) {
            System.out.println("Suppressed Exception");
        }
*** Executing Exception callback...
Exception callback: attempt 0
Attempts exhausted. Total: 1
Last exception: Test Exception

最后是我们的IOException

// we catch IOException to allow the program to continue
        System.out.println("\n*** Executing IO Exception callback...");
        try {
            retryTemplate.execute(new SpecificExceptionCallback(), new LoggingRecoveryCallback());
        } catch (IOException e) {
            System.out.println("Suppressed IO Exception");
        }
*** Executing IO Exception callback...
IO Exception callback: attempt 0
IO Exception callback: attempt 1
IO Exception callback: attempt 2
Attempts exhausted. Total: 3
Last exception: Test IO Exception

如我们所见,只有IOException启动了三个尝试。 请注意,尝试次数从0开始编号,因为执行回调时尝试次数并未耗尽,因此上一次尝试次数为#2,而不是#3。 但是在RecoveryCallback上所有尝试均已用尽,因此上下文保留3次尝试。

我们还可以看到,尝试成功后未调用RecoveryCallback 。 也就是说,仅当执行以异常结束时才调用它。

RetryTemplate是同步的,因此所有执行都发生在我们的主线程中。 这就是为什么我在调用周围添加了try / catch块的原因,以使程序可以毫无问题地运行所有三个示例。 否则,重试策略将在上次失败尝试后将异常抛出,并停止执行。

还有一个非常有趣的CompositeRetryPolicy ,它允许添加几个策略并委托来依次调用它们。 它还可以允许创建相当灵活的重试策略,但这本身就是另一个主题。

我认为spring-retry是一个非常有用的库,它可以使常见的可重试任务更可预测,可测试且更易于实现。

翻译自: https://www.javacodegeeks.com/2017/07/spring-retry-winter-coming.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值