springboot 重试机制实践

前言

在实际工作中,重处理是一个非常常见的场景,比如:

发送消息失败。
调用远程服务失败。
争抢锁失败。
这些错误可能是因为网络波动造成的,等待过后重处理就能成功。通常来说,会用try/catch,while循环之类的语法来进行重处理,但是这样的做法缺乏统一性,并且不是很方便,要多写很多代码。然而spring-retry却可以通过注解,在不入侵原有业务逻辑代码的方式下,优雅的实现重处理功能。

一、@Retryable是什么?

spring系列的spring-retry是另一个实用程序模块,可以帮助我们以标准方式处理任何特定操作的重试。在spring-retry中,所有配置都是基于简单注释的。

二、使用步骤

1.pom依赖

     
        org.springframework.retry
        spring-retry
        1.2.5.RELEASE
    

2 在服务启动类上加注解 @EnableRetry**

@EnableRetry
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

3接下来就是在需要重试的方法上面加注解@Retryable,需要注意的是由于是基于AOP实现,所以不支持类里自调用方法,一般把该重试方法写成一个service,通过service调用,不然重试不会生效

pscInvInfoService.ocrVerify(json);

下面是具体的方法

    @Override
    @Retryable(value = Exception.class, maxAttempts = 3, backoff = 
    @Backoff(delay = 2000L, multiplier = 1.5))
   
    public void ocrVerify(String jsonStr) throws Exception {
        // 该重试只针对网络问题导致的错误重试,不对发票验真失败的进行重试
        log.info("jsonStr" + jsonStr);
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, jsonStr);
        Request request = new Request.Builder()
                .url(verifyUrl)
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .build();
        Response response = client.newCall(request).execute();
        
        JSONObject result = JSONObject.parseObject(response.body().string());
        JSONObject S = JSONObject.parseObject(result.get("returnStateInfo").toString());
        String status = S.getString("returnCode");
        String msg = S.getString("returnMessage");
        String data = result.getString("data");
        if (POST_SUCCESS.equals(status)) {
            log.info("成功" + data);
            // 修改状态
            JSONObject js = JSONObject.parseObject(jsonStr);
            String invCode = js.getString("invoiceCode");
            String invNo = js.getString("invoiceNo");
            Date date = js.getDate("invoiceDate");
            pscInvInfoMapper.updateVerifyStatus(invNo, invCode, date);
            log.info("修改状态-成功"+DateTimeUtils.getCurrentDateTime(DateTimeUtils.DATETIME_PATTERN));
        } else {
            log.info("失败" + msg);

        }

    }

3.1 Retryable注解的参数具体含义
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所有异常
exclude:指定不处理的异常
maxAttempts:最大重试次数,默认3次
backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

三 当重试耗尽时还是失败,会出现什么情况呢?

当重试耗尽时,RetryOperations可以将控制传递给另一个回调,即RecoveryCallback。Spring-Retry还提供了@Recover注解,用于@Retryable重试失败后处理方法。如果不需要回调方法,可以直接不写回调方法,那么实现的效果是,重试次数完了后,如果还是没成功没符合业务判断,就抛出异常。

    @Recover
    public void verifyRecover(Exception e){
        System.out.println("=======重试次数到达最大值,重试结束===="+e.getMessage());
    }

可以看到传参里面写的是 Exception e,这个是作为回调的接头暗号(重试次数用完了,还是失败,我们抛出这个Exception e通知触发这个回调方法)。对于@Recover注解的方法,需要特别注意的是:

方法的返回值必须与@Retryable方法一致
方法的第一个参数,必须是Throwable类型的,建议是与@Retryable配置的异常一致,其他的参数,需要哪个参数,写进去就可以了(@Recover方法中有的)
该回调方法与重试方法写在同一个实现类里面

四 注意事项

1.由于是基于AOP实现,所以不支持类里自调用方法
2.如果重试失败需要给@Recover注解的方法做后续处理,那这个重试的方法不能有返回值,只能是void
3.方法内不能使用try catch,只能往外抛异常
4.@Recover注解来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中),此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。

五 重试场景

在这里插入图片描述
可以看到重试了三次并且最终执行了失败处理方法

你可能感兴趣的:(spring,boot,spring,java)