分享免费的编程资源和教程

网站首页 > 技术教程 正文

SpringRetry 一种优雅的重试方式 spring重试机制

goqiw 2024-09-25 20:12:57 技术教程 89 ℃ 0 评论

在程序的运行过程中一定会出现由于某些原因发生意外的情况,这种时候我们可以通过重新执行来确保程序能够执行成功,Spring框架中有一个小组件spring-retry提供了自动重试的功能。这在错误可能是短暂的(例如瞬间的网络故障)情况下非常有帮助。如果使用原生的Java写法,我们该如何处理重试的呢?

//重试次数
int retryCount = 3;
//重试间隔
int retryInterval = 1000;
while (retryCount > 0) {
    try {
        //todo 执行业务逻辑
        return doSomething();
    } catch (Exception e) {
        //打印异常信息
        System.out.println(e.getMessage());
        //重试次数减1
        retryCount--;
        //等待重试间隔
        Thread.sleep(retryInterval);
    }
}

上面的代码虽然也可以实现重试的功能,但是代码显得比较臃肿,而且重试的代码和业务代码耦合在一起,如果以后需要修改重试的次数或者重试间隔,就需要修改代码,这样显然是不合理的,那么有没有更好的方式呢?这一次我们介绍使用SpringRetry组件实现重试功能。

1.引入依赖

在pom文件中添加spring-retry和aop的依赖:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.7</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.1.11</version>
</dependency>

通过spring-retry实现重试的方式有两种,一种是声明式使用注解,另一种是编程式。

2.声明式

在需要重试的方法上添加@Retryable注解,同时在配置类上加@EnableRetry注解启用重试。下面是一个示例:

启动类

@SpringBootApplication
@EnableRetry

在service中添加方法,方法上添加@Retryable注解,传递一个string类型的参数,当参数为error时模拟发生故障抛出异常。

/**
 * @Retryable注解标记需要重试的方法
 * retryFor:指定捕获的异常类型
 * maxAttempts:最大重试次数
 * backoff:重试间隔配置信息
 * delay:重试间隔
 * multiplier:间隔倍率,间隔倍率是指第一次间隔为delay,第二次间隔为delay*multiplier,第三次间隔为delay*multiplier*multiplier,以此类推
 */
@Retryable(retryFor = Exception.class,maxAttempts = 5,backoff = @Backoff(delay = 500,multiplier=2))
public String test(String msg){
    System.out.println(System.currentTimeMillis());
    if ("error".equals(msg)){
        int i = 1/0;
    }
    return "success";
}
//output
//1722519362752
//1722519363260
//1722519364272
//1722519366281
//1722519360283

从输出结果可以看出,当程序发生异常时,第二次重试间隔为500ms,第三次重试间隔为1000ms,第四次重试间隔为2000ms,第五次重试间隔为4000ms,以此类推。

也可以通过@Recover注解标记一个方法作为兜底方案,当重试次数达到最大次数后还是执行失败,会调用@Recover注解标记的方法。@Recover注解标记的方法的返回值类型必须和@Retryable注解标记的方法一致,@Recover必须和@Retryable标记的方法在同一个类中。下面是一个示例:

@Recover
public String recover(String msg){
    System.out.println(msg);
    System.out.println("兜底方法");
    return "recover";
}


3.编程式

下面是一个示例:

在配置类中创建一个RetryTemplateBean,交给Spring管理。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.*;
import org.springframework.retry.support.RetryTemplate;


@Configuration
public class RetryConfig {
    @Bean
    public RetryTemplate retryTemplate(){
        //指定退避策略
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000L);
        //指定重试策略
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        //创建重试模板
        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setBackOffPolicy(backOffPolicy);
        retryTemplate.setRetryPolicy(retryPolicy);
        return retryTemplate;
    }
}

在service中注入RetryTemplate,调用execute方法执行需要重试的方法。下面是一个示例:

public void test2(int a) {
    try {
        Object execute = retryTemplate.execute((RetryCallback<Object, Throwable>) context -> {
            //todo 业务逻辑
            return a;
        });
    } catch (Throwable e) {
        //todo 处理最终异常
        throw new RuntimeException(e);
    }
}

如果在业务逻辑处理的过程中发生了异常,会自动进行重试,在这个例子中会重试3次,每次间隔时间为1秒。如果重试3次后还是发生异常,会抛出异常,可以在catch中处理最终异常。

也可以在retryTemplate中添加监听器,监听器会在重试的过程中提供额外的回调,可以利用这些回调处理不同重试中的各种情况,下面是一个示例:

在创建RetryTemplate时注册监听器,监听器重写之后可以重写RetryListener接口中的open,close,onError,onSuccess方法,分别对应重试开始,重试结束,重试发生异常,重试成功时调用。我们这次重写onError方法,当发生异常时打印重试次数。

retryTemplate.registerListener(new RetryListener() {
    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        System.out.println("发生异常,重试次数:" + context.getRetryCount());
    }
});
//output
//发生异常,重试次数:1
//发生异常,重试次数:2
//发生异常,重试次数:3


下面是对RetryTemplate的简单介绍:

  • RetryTemplate封装了Retry的基本操作,可以指定重试策略,退避策略,监听器等。
  • BackOffPolicy定义了重试的退避策略,下面是对它常用的实现类的解释。
    • FixedBackOffPolicy:每次重试间隔固定时间。
    • ExponentialBackOffPolicy:每次重试间隔时间指数增长。
    • NoBackOffPolicy:不进行退避,每次重试间隔时间为0。
    • UniformRandomBackOffPolicy:随机间隔时间,每次重试间隔时间为一个随机数。
  • RetryPolicy定义了重试的策略,可以指定重试的最大次数,重试的条件等。
    • SimpleRetryPolicy:简单的重试策略,可以指定重试的最大次数和捕获的异常。
    • CircuitBreakerRetryPolicy:断路器重试策略,可以防止重试次数过多导致系统崩溃。
    • TimeoutRetryPolicy:超时重试策略,可以防止重试时间过长导致系统崩溃。
    • CompositeRetryPolicy:组合重试策略,可以组合多个重试策略一起使用。
  • RetryListener定义了重试的监听器,可以监听重试的开始,结束,异常,成功等事件。

4.应用场景

当程序因为偶尔的网络抖动,数据库锁的获取,消息队列的消费端因为资源不足,调用三方应用时因为对方系统繁忙导致请求失败等原因。

spring-retry提供简单易用的重试机制,可以提高系统的可靠性和健壮性。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表