失败重试方案spring-retry

在后台对接第三方接口时,由于网络波动等原因,经常会出现请求超时的情况,但是有些接口不能因为一次超时就判定为失败,通常都会有重试,比如连续请求三次都失败,才认为请求失败。

方案一:在代码中进行逻辑判断

以前在处理这类问题的时候,我们大多采用的是这种方案。即设置一个重试次数,然后每次调用后判断是否调用成功,如果调用失败且没有超过重试次数,则重试调用。如下

@Test
void invoke() {
    int failCount = 0;
    while (failCount < 3) {
        if (doInvoke()) {
            break;
        }
        failCount++;
    }
    // 连续三次失败,记录失败日志
    // logService.saveLog(...)
}

boolean doInvoke() {
    // 模拟远程调用
    boolean b = new Random().nextBoolean();
    log.info("doInvoke result : {}", b);
    return b;
}

方案二:框架自带重试机制

在微服务架构中,几乎所有框架都支持超时重试,如dubbo、springcloud的feign都有超时重试机制。作为服务的调用方,我们可以不考虑接口的幂等性,默认为提供方已经做好了幂等处理,这时候我们只需要设置一下超时时间和重试次数即可。如

@DubboReference(retries = 2)
private OrderInfoService orderInfoService;

方案三:使用spring retry

spring retry原先是springbatch中的一个模块,后来被独立出来,使用非常简单,也是本文主要介绍的用法。

需要添加如下maven依赖,然后在启动类或配置类上添加@EnableRetry注解

<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>

只需要重点关注其中的两个注解@Retryable@Recover,前者指定重试次数和重试生效条件以及重试时间间隔等,后者一般作为失败后的日志记录或失败补偿。

使用示例如下:

@Slf4j
@Service
public class PayService {

    long start = 0;

    @Retryable(recover = "recoverTest",
            value = IllegalArgumentException.class,
            maxAttempts = 10,
            backoff = @Backoff(value = 1000L, maxDelay = 5000L, multiplier = 1.5))
    public boolean pay() {
        if (start == 0) {
            start = System.currentTimeMillis();
        }
        // 记录前后间隔时间
        log.info("pay....{}", System.currentTimeMillis() - start);
        start = System.currentTimeMillis();
        throw new IllegalArgumentException("问题不大...");
    }

    @Recover
    public boolean recoverTest(IllegalArgumentException e) {
        log.info("recover...{}", e.getMessage());
        return false;
    }
}

其中,@Retryablerecover指定了所有重试均失败后的回调方法,如果@Retryable标注方法调用成功了,则不会调用@Recover注解标注的方法,与原方法的返回类型一样,@Recover注解标注的方法入参与@Retryable注解标注方法中的valueinclude属性指定的异常类型对应,maxAttempts指定最大重试次数,默认为3,backoff指定重试时间间隔等,如上述示例中,重试间隔时间每次都是上一次的1.5倍,且最大不超过5秒。

执行后日志如下

2022-02-16 10:51:03.045  INFO 9224 --- [           main] com.example.service.PayService           : pay....0
2022-02-16 10:51:04.046  INFO 9224 --- [           main] com.example.service.PayService           : pay....1000
2022-02-16 10:51:05.546  INFO 9224 --- [           main] com.example.service.PayService           : pay....1500
2022-02-16 10:51:07.796  INFO 9224 --- [           main] com.example.service.PayService           : pay....2250
2022-02-16 10:51:11.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....3375
2022-02-16 10:51:16.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....5000
2022-02-16 10:51:21.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....5000
2022-02-16 10:51:26.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....5000
2022-02-16 10:51:31.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....5000
2022-02-16 10:51:36.171  INFO 9224 --- [           main] com.example.service.PayService           : pay....5000
2022-02-16 10:51:36.171  INFO 9224 --- [           main] com.example.service.PayService           : recover...问题不大...

@Retryable注解中还有其他属性,如支持表达式的exceptionExpression,例如在该注解中添加exceptionExpression = "message.contains('出大问题啦...')",则将不会进行异常重试,因为异常错误信息不匹配。至于其他的属性,就不再举例了,很少用到,上面这些个属性几乎足够用了。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值