使用Spring RetryTemplate优雅的进行重试

1、使用场景

  • 在我们与下游通过http进行数据交互时,会约定接口协议,比如:双方约定返回20000时,可以重新请求获取正确的结果。
  • 在进行http进行网络通信时,经常会发生一些网络层面的异常如:IOException:unexpected end of stream on Connection;SocketException:Broken pipe (Write failed)、Connection reset等,比较懒的做法是加上重试逻辑,如果你是技术宅,可以深究里面的深层原因。
  • 其它任何可以重试的场景

2、常规的重试写法

    int retryTimes = 3;
    for (int i = 0; i < retryTimes; i++) {
        Result result = doSomethingPre();
        if (result == 预期值){
            doSomething();
            berak;
        }
    }

有没有觉得这段代码就像一坨shit,如果这种重试需求很多,那么打开项目就会有满屏shit。做为一个有追求的码农,不能容忍这种事情发生,接下来Spring RetryTemplate闪亮登场。

3、Spring RetryTemplate的使用

  • 引入依赖
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.4.RELEASE</version>
</dependency>
  • 配置
    既要重试,我们肯定要定义好重试的策略:重试的触发条件,重试次数,时间间隔等要素。

RetryPolicy


下图是RetryPolicy及实现类的关系图,太大了,看不清,没关系,我们只看最简单最常用的SimpleRetryPolicy,它里面可以设置重试次数:setMaxAttempts();不设置的话,默认值是3次。

BackOffPolicy

顾名思义BackOffPolicy是退避策略,看一下他的实现类。

用ExponentialBackOffPolicy来举例子,它是指数退避策略,需设置参数
initialInterval:初始休眠时间,默认100毫秒
multiplier:指定乘数,当前休眠时间*multiplier即为下一次的休眠时间;
maxInterval:指定最大休眠时间,默认30秒,避免multiplier过大引起无限期等待。

RetryTemplate

spring容器中注入RetryTemplate这个bean,并在该bean中设置上述两个策略:

    @Bean
    public RetryTemplate simpleRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setRetryPolicy(new SimpleRetryPolicy());
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(3000);
        backOffPolicy.setMultiplier(2);
        backOffPolicy.setMaxInterval(15000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }

上面的配置通俗的说就是,最大重试三次,时间间隔为3,32,32*2,且时间间隔不大于15s。

  • 使用
    retryTemplate.execute(RetryCallback<T, E> retryCallback);
    RetryCallback接口只有一个接口,即为函数式接口,可以使用lamda表达式来写,简洁且直观。
    最终写法:retryTemplate.execute(retryContext -> {重试逻辑代码});
    这段为源代码,可以省略,可以看到最终调用为RetryCallback接口的实现类,及lamda表达式
    @Override
    public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E {
	return doExecute(retryCallback, null, null);
    }

    protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,RecoveryCallback<T> recoveryCallback, RetryState state)throws E, ExhaustedRetryException {
        ......
        return retryCallback.doWithRetry(context);
    }

上述是retryTemplate最简单的用法,它还有很多很强大的用法,有时间再来补充。

posted @ 2020-03-24 15:25  园中小卒  阅读(9631)  评论(0编辑  收藏  举报