Spring Retry使用总结(一)

1. 介绍
在项目中,调用第三方接口响应比较慢,或者由于网络抖动等原因,导致无响应的情况,就要用到重试机制.比较简单成熟的方案就是使用spring-retry功能,spring-retry需要使用aop的特性,所以引入aspectj。
2. 项目依赖

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

3、spring对于重试机制的实现,给了几个抽象。

  • BackOff:补偿值,一般指失败后多久进行重试的延迟值。
  • Sleeper:暂停应用的工具,通常用来应用补偿值。
  • BackOffPolicy:补偿策略,决定失败后如何确定补偿值。
  • RetryContext:重试上下文,代表了能被重试动作使用的资源。
  • RetryPolicy:重试策略,决定失败能否重试。
  • RecoveryCallback:定义一个动作recover,在重试耗尽后的动作。
  • RetryCallback:具体的重试动作。
  • RetryOperations:通过传递RetryCallback,进行重试操作。
  • RetryState:重试状态,通常包含一个重试的键值。
  • RetryStatistics和RetryListener,用来监控Retry的执行情况,并生成统计信息。

4、代码示例

    @Retryable(value= {Exception.class}, maxAttempts = 3)
    public void call() throws Exception {
        System.out.println("do something...");
        throw new Exception("RPC调用异常");
    }

    @Recover
    public void recover(RemoteAccessException e) {
        System.out.println(e.getMessage());
    }
    @Retryable(maxAttempts = 3, backoff = @Backoff(value = 3000, multiplier = 1.5))
    public Customer getCustomer(String customerId) {
        if (true) {
            JSONArray data = retObj.getJSONArray("data");
            if (data != null && !data.isEmpty()) {
                return data.toJavaList(Customer.class).get(0);
            }
        } else {
            log.error("异常,{}", customerId);
            throw new RuntimeException("获数据失败");
        }
        return null;
    }

@Retryable被注解的方法发生异常时会重试。
@Retryable注解中的参数说明:

  • maxAttempts :最大重试次数,默认为3,如果要设置的重试次数为3,可以不写;
  • value:抛出指定异常才会重试
  • include:和value一样,默认为空,当exclude也为空时,所有异常都重试
  • exclude:指定不处理的异常,默认空,当include也为空时,所有异常都重试
  • backoff:重试等待策略,默认使用@Backoff@Backoff的value默认为1000L,我们设置为2000L。

@Backoff重试补偿机制,默认没有
@Backoff注解中的参数说明:

  • value:隔多少毫秒后重试,默认为1000L,我们设置为3000L;
  • delay:和value一样,但是默认为0;
  • multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

5、@Recover注解
可以在指定方法上标记@Recover来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中)
@Recover:
当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。

6、采坑提示
1、由于retry用到了aspect增强,所有会有aspect的坑,就是方法内部调用,会使aspect增强失效,那么retry当然也会失效。参考改链接;

public class demo {
    public void A() {
        B();
    }

    //这里B不会执行
    @Retryable(Exception.class)
    public void B() {
        throw new RuntimeException("retry...");
    }
}

2、maxAttemps参数解释的是说重试次数,但是我再打断点的时候发现这个=1时,方法一共只执行了一次。
3、recover回调报错

org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method报错显示找不到recovery方法

解决recover回调报错的方案就这这两句话:

  • 异常类型需要与Recover方法参数类型保持一致
  • recover方法返回值需要与重试方法返回值保证一致

补充
对于非幂等的请求(比如新增,更新操作),千万不要使用重试,对数据一致性会造成很大影响。

郭慕荣博客园
原文地址:https://www.cnblogs.com/jelly12345/p/15292568.html