spring中重试机制详解

一、@Retryable注解

注解方式实现重试机制比较简单,只需要我们在需要重试的方法上加入以下注解

@Retryable(value = {RemoteAccessException.class},    maxAttempts = 3,    backoff = @Backoff(delay = 1000))
  • value:指定需要重试的异常类型。在这个例子中,我们指定了 RemoteAccessException 类型的异常。
  • maxAttempts:指定最大重试次数。在这个例子中,我们指定了最大重试次数为 3。
  • backoff:指定重试之间的时间间隔。在这个例子中,我们指定了 1000 毫秒的时间间隔。

 当然异常类型可以写成Exception.class,这样当方法抛出任何异常都会重试了。

使用注解的方式实现重试机制需要注意以下几点:

1.首先是启动类加上@EnableReTry注解

2. 要求需要重试的方法和调用它的方法不能同属一个类中,因为@Retryable注解是通过切面实现的,说白了就是带有@Retryable注解的方法和调用他的方法都要被spring管理,属于不同的类即可。

3.@Retryable只能实现重试机制,重试次数达到上限后,还是失败,此时如果有需要回调处理的逻辑,需要另外一个注解@Recover

@Retryable(value = {SQLException.class}, maxAttempts = 3, backoff = @Backoff(delay = 100))
public void myMethod() throws SQLException {
    // Some database operation that may throw a SQLException
}
 
@Recover
public void recover(SQLException e) {
    // Recovery logic goes here
}

要求@Retryable@Recover必须定义在一个类当中,这一点和上面调用@Retryable的方法必须分属不同的类正好相反,同时也要求@Retryable注解的方法不能有返回值,否则@Recover不生效

那如果一个类中有多个@Recover和@Retryable怎么区分?

很简单,可以通过value属性来区分它们。value属性允许您指定一个异常类型的数组,以区分在方法执行期间抛出的不同异常类型。

具体示例如下:

@Service
public class MyService {
 
    @Retryable(value = {IOException.class})
    public void methodA() throws IOException {
        // Some code that may throw an IOException
    }
 
    @Recover
    public void recoverA(IOException e) {
        // Recovery logic for methodA() goes here
    }
 
    @Retryable(value = {NullPointerException.class})
    public void methodB() throws NullPointerException {
        // Some code that may throw a NullPointerException
    }
 
    @Recover
    public void recoverB(NullPointerException e) {
        // Recovery logic for methodB() goes here
    }
}

笔者不想再单独写类了,想让调用@Retryable注解的方法和带有@Retryable注解本身的方法,两个方法属于一个类中,但是此时@Retryable不生效了,所以搜寻了另外一种方式,通过编码方法实现方法重试,当然也是spring提供的工具了,自己封装也不是不行,就是没那么好,考虑的不全面,请看第二种方式。

二、通过RetryTemplate

这种方式也是要求在启动类上添加@EnableReTry注解,如上图所示

1.RetryTemplate的配置类

package com.ruoyi.maintenance.thirdinterface.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

/**
 * 重试配置类
 * @author hulei
 */
@Configuration
@EnableRetry
public class RetryConfiguration {
    @Bean
    public RetryTemplate retryTemplate() {
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        //最大重试次数
        retryPolicy.setMaxAttempts(5);
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        //倍数
        backOffPolicy.setMultiplier(2);
        //初始间隔
        backOffPolicy.setInitialInterval(3000);
        RetryTemplate template = new RetryTemplate();
        template.setRetryPolicy(retryPolicy);
        template.setBackOffPolicy(backOffPolicy);
        return template;
    }
}

配置类主要是设置一些重试次数和重试时间等参数

有时候为了灵活使用,需要手动设置参数,这里提供了一个构建器 RetryTemplateBuilder

package com.ruoyi.maintenance.thirdinterface.builder;

import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

/**
 * 重试模板构建器
 * @author hulei
 */
public class RetryTemplateBuilder {

    /**
     * 最大重试次数
     */
    private int maxAttempts;

    /**
     * 重试间隔倍数(即第一次重试间隔为initialInterval,第二次重试间隔为initialInterval * backOffMultiplier,第三次重试间隔为initialInterval * backOffMultiplier^2)
     */
    private double backOffMultiplier;


    /**
     * 初始间隔
     */
    private long initialInterval;

    public RetryTemplateBuilder withMaxAttempts(int maxAttempts) {
        this.maxAttempts = maxAttempts;
        return this;
    }

    public RetryTemplateBuilder withBackOffMultiplier(double backOffMultiplier) {
        this.backOffMultiplier = backOffMultiplier;
        return this;
    }

    public RetryTemplateBuilder withInitialInterval(long initialInterval) {
        this.initialInterval = initialInterval;
        return this;
    }

    public RetryTemplate build() {
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(maxAttempts);

        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setMultiplier(backOffMultiplier);
        backOffPolicy.setInitialInterval(initialInterval);

        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);

        return retryTemplate;
    }
}

使用如下

2.重试代码

我要重试的方法如下图

调用它的方法如下

 引入retryTemplate

 主要代码片段

 try{
                String result = retryTemplate.execute(
                        //RetryCallback,需要重试的业务逻辑
                        (RetryCallback<String, Throwable>) retryContext -> doPost(url, rsaEncryptHandle(jsonHandle(jsonObject,loop,platform).toJSONString())),
                        //RecoveryCallback兜底,多次重试失败后的处理逻辑,取最后一次重试抛出的异常信息,抛出异常
                        retryContext -> {
                            log.error("删除设备推送接口多次重试调用失败");
                            throw new RuntimeException(retryContext.getLastThrowable().getMessage());
                        }
                );
                log.info("删除设备远程调用返回结果:{}",result);
            }catch (Throwable e){
                log.error("删除远程调用异常:{}",e.getMessage());
            }

核心是retryTemplate.excute调用

有两个参数RetryCallback和RecoveryCallback

 1.RetryCallback 

包装需要重试的逻辑,比较简单就是调用了我需要重试的方法doPost()

2.RecoveryCallback

重试次数达到上限,任然没有成功时的回调方法

笔者这里只是打印了并抛出了异常信息,如果有其他需要处理的异常回调逻辑,单独写一个方法,在这里调用即可。

总结:两种方式各有好处,看个人选择,如果需要编写的重试方法较多,建议使用注解方式,省时省力,单一场景使用,不想再写过多的类,使用第二种编码的方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值