spring-retry详解

什么是重试

重试是指,当在一个程序运行过程中,突然遇到了例如网络延迟,中断等情况时,为了保证程序容错性,可用性,一致性等的一个措施,目前主流的框架大多都有一套自己的重试机制,例如 dubbo,mq,Spring 等

概要

Spring 也自己实现了一套重试机制,Spring Retry 是从 Spring batch 中独立出来的一个功能,主要功能点在于重试和熔断,目前已经广泛应用于 Spring Batch,Spring Integration, Spring for Apache Hadoop 等 Spring 项目。spring retry 提供了注解和编程 两种支持,提供了 RetryTemplate 支持,类似 RestTemplate。 整个流程如下:

使用介绍

Maven 依赖

java 复制代码
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<!-- also need to add Spring AOP into our project-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency`>`

注解使用

开启 Retry 功能,需在启动类中使用 @EnableRetry 注解

java 复制代码
@SpringBootApplication
@EnableRetry
@EnableScheduling
public class DemoApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
​
​
}

注解 @Retryable

需要在重试的代码中加入重试注解 @Retryable

java 复制代码
 @Retryable(value = RuntimeException.class)
    public void testRetry01() throws MyException {
        System.out.println("测试-value属性");
        throw new RuntimeException("出现了异常");
    }

默认情况下,会重试 3 次,间隔 1 秒

重试配置

通过 @Retryable 注解的属性 可以实现重试配置

  • Value()

  • include

    value 与 include 含义相同,表示可重试的异常类型。默认为空,如果同时 exclude 也为空则会重试所有异常。但在使用时需要注意

js 复制代码
@Retryable(value = RuntimeException.class)
    public void testRetry01() throws MyException {
        System.out.println("测试-value属性");
        throw new RuntimeException("出现了异常");
    }

例:testRetry01 只会在程序抛出 RuntimeException 时,开启重试

  • exclude

不可重试的异常类型。默认为空(如果 include 也为为空,将重试所有异常)。如果 include 为空但 exclude 不为空,则重试非 exclude 中的异常

csharp 复制代码
@Retryable(exclude = RuntimeException.class)
    public void testRetry02() throws MyException {
        System.out.println("测试-value属性");
        throw new MyException("出现了异常");
    }

例:testRetry02 在程序抛出 MyException 时,不会开启重试

  • maxAttempts

最大重试次数,默认为 3

  • maxAttemptsExpression

最大尝试次数的表达式,表达式一旦设置了值,则会覆盖 maxAttempts 的值,maxAttemptsExpression 可以读取 application.yml 配置文件里的数据,也可以通过 SpEL 表达式计算对应的值

js 复制代码
 @Retryable(value = MyException.class, maxAttemptsExpression = "${maxAttempts}")
    public void testRetry03() throws MyException {
        System.out.println("测试-maxAttemptsExpression属性");
        throw new MyException("出现了异常");
    }

例:testRetry03 会去读 properties 配置文件获取属性名为 maxAttempts 的值

js 复制代码
  @Retryable(value = MyException.class,  maxAttemptsExpression = "#{2+3}")
    public void testRetry04() throws MyException {
        System.out.println("测试-maxAttemptsExpression属性");
        throw new MyException("出现了异常");
    }
​
  • exceptionExpression

异常处理表达式,ExpressionRetryPolicy 中使用,执行完父类的 canRetry 之后,需要校验 exceptionExpression 的值,为 true 则可以重试

复制代码
@Retryable(value = MyException.class, exceptionExpression = "#{@retryService.isRetry()}")
    public void testRetry05() throws MyException {
        System.out.println("测试-exceptionExpression");
        throw new MyException("出现了异常");
    }

例:这个表达式的意思就是,如果 testRetry05 方法出现异常 会调用 retryService.isRetry() 方法,根据返回结果判断是否重试

  • @Recover兜底方法

  • 当 @Retryable 方法重试失败之后,最后就会调用 @Recover 方法。用于 @Retryable 失败时的"兜底"处理方法。 @Recover 的方法必须要与 @Retryable 注解的方法保持一致,第一入参为要重试的异常,其他参数与 @Retryable 保持一致,返回值也要一样,否则无法执行!

js 复制代码
    @Retryable(value = MyException.class)
    public void testRetry06() throws MyException {
        System.out.println("测试兜底方法");
        throw new MyException("出现了异常");
    }

    @Recover
    public void recover06(MyException e) {
        System.out.println("兜底方法开启,异常信息:" + e.getMessage());
    }

熔断模式@CircuitBreaker

指在具体的重试机制下失败后打开断路器,过了一段时间,断路器进入半开状态,允许一个进入重试,若失败再次进入断路器,成功则关闭断路器,注解为 @CircuitBreaker ,具体包括熔断打开时间、重置过期时间

js 复制代码
@CircuitBreaker(openTimeout = 1000, resetTimeout = 3000, value = MyException.class)
public void testRetry07() throws MyException {
    System.out.println("测试CircuitBreaker注解");
    throw new MyException("出现了异常");
}

例:openTimeout 时间范围内失败 maxAttempts 次数后,熔断打开 resetTimeout 时长 这个方法的意思就是方法在一秒内失败三次时,触发熔断,下次在有请求过来时,直接进入

重试策略

  • SimpleRetryPolicy 默认最多重试 3 次

  • TimeoutRetryPolicy 默认在 1 秒内失败都会重试

  • ExpressionRetryPolicy 符合表达式就会重试

  • CircuitBreakerRetryPolicy 增加了熔断的机制,如果不在熔断状态,则允许重试

  • CompositeRetryPolicy 可以组合多个重试策略

  • NeverRetryPolicy 从不重试(也是一种重试策略哈)

  • AlwaysRetryPolicy 总是重试

退避策略

退避策略退避是指怎么去做下一次的重试,在这里其实就是等待多长时间。

通过 @Backoff 注解实现,那么我们首先看一下@Backoff 的参数

@Backoff 参数

  • value

默认为 1000, 与 delay 作用相同,表示延迟的毫秒数。当 delay 非 0 时,此参数忽略。

  • delay

默认为 0。在指数情况下用作初始值,在统一情况下用作*的最小值。当此元素的值为 0 时,将采用元素 value 的值,否则将采用此元素的值,并且将忽略 value。

  • maxDelay

默认为 0。重试之间的最大等待时间(以毫秒为单位)。如果小于 delay,那么将应用默认值为 30000L

  • multipler

默认为 0。如果为正,则用作乘法器以生成下一个退避延迟。返回一个乘法器,用于计算下一个退避延迟

  • delayExpression

评估标准退避期的表达式。在指数情况下用作初始值*,在均匀情况下用作最小值。覆盖 delay。

  • maxDelayExpression

该表达式计算重试之间的最大等待时间(以毫秒为单位)。 如果小于 delay,那么将应用 30000L 为默认值。覆盖 maxDelay。

  • multiplierExpression

评估为用作乘数的值,以生成退避的下一个延迟。覆盖 multiplier。 返回一个乘数表达式,用于计算下一个退避延迟

  • random

默认为 false,在指数情况下 multiplier> 0 将此值设置为 true 可以使后退延迟随机化,从而使最大延迟乘以前一延迟,并且两个值之间的分布是均匀的。

js 复制代码
@Retryable(value = MyException.class, maxAttempts = 4,
        backoff = @Backoff(delay = 2000, multiplier = 2, maxDelay = 5000))
public void testRetry08() throws MyException {
    System.out.println("测试-backoff属性");
    throw new MyException("出现了异常");
}

@Backoff 的参数会影响我们使用哪种退避策略

  • FixedBackOffPolicy

默认退避策略,每 1 秒重试 1 次

  • ExponentialBackOffPolicy

指数退避策略,当设置 multiplier 时使用,每次重试时间间隔为 当前延迟时间 * multiplier。

例如:默认初始 0.1 秒,系数是 2,那么下次延迟 0.2 秒,再下次就是延迟 0.4 秒,如此类推,最大 30 秒。

  • ExponentialRandomBackOffPolicy

指数随机退避策略。在指数退避策略的基础上增加了随机性。

  • UniformRandomBackOffPolicy

均匀随机策略,设置 maxDely 但没有设置 multiplier 时使用,重试间隔会在 maxDelay 和 delay 间随机

相关推荐
星辰离彬24 分钟前
Java 与 MySQL 性能优化:MySQL连接池参数优化与性能提升
java·服务器·数据库·后端·mysql·性能优化
超级小忍37 分钟前
Spring Boot 与 Docker 的完美结合:容器化你的应用
spring boot·后端·docker
麦兜*2 小时前
Spring Boot 企业级动态权限全栈深度解决方案,设计思路,代码分析
java·spring boot·后端·spring·spring cloud·性能优化·springcloud
程序员爱钓鱼4 小时前
Go语言实战案例-读取用户输入并打印
后端·google·go
Livingbody8 小时前
基于【ERNIE-4.5-VL-28B-A3B】模型的图片内容分析系统
后端
你的人类朋友9 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
追逐时光者10 小时前
面试第一步,先准备一份简洁、优雅的简历模板!
后端·面试
慕木兮人可10 小时前
Docker部署MySQL镜像
spring boot·后端·mysql·docker·ecs服务器
发粪的屎壳郎10 小时前
ASP.NET Core 8 轻松配置Serilog日志
后端·asp.net·serilog