介绍
Spring Retry是Spring框架提供的用于处理重试操作的模块。它旨在简化在应用程序中处理失败和异常情况的重试逻辑。
使用
-
引入依赖
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.1</version> </dependency>
-
启用重试
在Spring配置类上添加@EnableRetry注解,启用重试功能。
-
使用重试
- @Retryable注解方式
- RetryTemplate模板方式
注解方式:@Retryable
当使用@Retryable注解的方法在执行时发生异常时,Spring将自动重试该方法,直到方法成功执行或达到最大重试次数。
Retryable注解的属性
-
interceptor
说明:该属性用于指定一个重试拦截器。重试拦截器可以在每次重试之前或之后执行特定操作,例如记录日志、执行某些额外的逻辑等。通过指定拦截器,可以对重试过程进行更精细的控制和扩展。
注意:interceptor属性与其它属性是互斥的。
用途:通常用于实现自定义的重试逻辑、日志记录、性能统计等。
-
value
说明:该属性用于指定触发重试的异常类型。它是一个Class类型的数组,可以指定一个或多个异常类。只有当方法抛出指定类型的异常时,才会触发重试机制。
注意:value与include类型类似,优先级高于include。默认为空(如果exclude排除也为空,则重试所有异常)
用途:用于定义哪些异常应该触发重试策略,可以根据具体业务需求和异常类型进行设置。
-
include
说明:该属性用于指定必须包含在内的异常类型,只有这些异常会触发重试。与value属性相似,但优先级低于value。如果同时指定了include和value,则以value为准。
注意:include与value类型类似,优先级低于include。默认为空(如果exclude排除也为空,则重试所有异常)
用途:在某些情况下,你可能只想对特定的异常进行重试,而不是对所有异常都进行重试。
-
exceptionExpression
说明:该属性是一个SpEL(Spring Expression Language)表达式,用于进一步定义哪些异常应该触发重试。它允许根据异常的详细信息进行更灵活的控制。
用途:用于根据自定义表达式来决定是否进行重试,例如根据异常的特定属性、消息内容等。
-
exclude
说明:该属性用于指定需要排除的异常类型,即使它们匹配了value、include或exceptionExpression指定的条件,也不会触发重试。
注意:如果include为空但exclude不为空,则重试所有未排除的异常。
用途:用于明确排除某些异常,确保它们不会触发重试机制。
-
label
说明:为重试策略提供一个标签。这个标签可以用于日志、监控或其他目的,以区分不同的重试策略。
用途:帮助在多个重试策略中进行标识和区分。
-
stateful
说明:表示该重试是有状态的还是无状态的。默认为false(无状态)。在有状态重试中,每次尝试可能会受之前尝试的影响。
用途:适用于那些前一次尝试的结果会影响下一次尝试的场景。
-
maxAttempts
说明:该属性用于设置最大重试次数(包括第1次调用)。它表示在方法连续失败的情况下,最多可以尝试多少次重试。当达到最大重试次数后,如果方法仍然失败,将不再继续重试。
用途:用于控制重试的次数,避免无限循环重试导致的资源浪费和性能问题。
-
maxAttemptsExpression
说明:与maxAttempts功能类似,但它接受一个SpEL表达式,用于动态地计算最大重试次数。
用途:为最大重试次数提供动态配置能力,根据实际运行时的参数、环境变量等来决定最大重试次数。
-
backoff
说明:该属性用于配置退避策略,即两次连续重试之间的延迟策略等。常见的策略包括固定延迟和指数延迟。属性指定一个@Backoff注解
Backoff注解的属性:
- value:用于指定重试间隔的起始值(默认为 1000 毫秒)。
- delay:用于指定固定的重试间隔(默认为 0)。
- maxDelay:用于指定重试间隔的最大值(默认为 0,表示没有最大间隔限制)。
- multiplier:用于计算下一个重试间隔的倍数(默认为 0,表示不使用指数级增长)。
- maxDelayExpression:用于指定重试间隔的最大表达式,允许你使用 SpEL 表达式动态地计算最大的重试间隔。
- multiplierExpression:用于指定重试间隔的倍数表达式,允许你使用 SpEL 表达式动态地计算重试间隔的倍数。
- random:用于指定是否使用随机间隔时间(默认为 false)。
用途:通过设置合适的退避策略,可以避免频繁的重试对系统造成过大压力,同时合理分散请求,提高系统的可用性和稳定性。
-
listeners
说明:定义重试事件的监听器。这些监听器可以捕获到重试的开始、结束、失败等事件,并据此执行相应的操作。
用途:用于实现自定义的重试事件处理逻辑,如发送通知、记录详细日志等。
Retryable注解的使用
-
定义重试方法:在您希望进行重试的方法上添加@Retryable注解。
@Retryable注解的属性:
- value:指定需要重试的异常类型。可以是单个异常类型或异常类型数组。
- maxAttempts:指定最大重试次数。
- backoff:指定重试间隔和退避策略。
-
定义重试失败处理方法:如果达到最大重试次数但仍然失败,可以使用@Recover注解在另一个方法中定义重试失败的处理逻辑。
@RestController
@RequestMapping(value = "/retry")
public class RetryController {
@Autowired
private RetryTest retryTest;
@GetMapping(value = "testAnnotation")
public ApiResult test() {
retryTest.test();
return ApiUtil.success();
}
}
@EnableRetry
@Component
public class RetryTest {
@Retryable(
value = {Exception.class}, // 指定需要重试的异常类型
maxAttempts = 3, // 最大重试次数
backoff = @Backoff(delay = 1000, multiplier = 2) // 重试间隔和退避策略
)
public void test() {
System.out.println("RetryTest.test() 调用"+ DateUtil.formatDateTime(new Date()));
int i = 1 / 0;
}
@Recover
public void recover(Exception e) {
System.out.println("RetryTest.test() 重试仍然失败了");
System.out.println(e);
}
}
日志
RetryTest.test() 调用2023-11-13 22:17:03
RetryTest.test() 调用2023-11-13 22:17:04
RetryTest.test() 调用2023-11-13 22:17:06
RetryTest.test() 重试仍然失败了
java.lang.ArithmeticException: / by zero
模板方式:RetryTemplate
使用RetryTemplate模板实现重试。
@Configuration
public class RetryTemplateConfig {
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
// 定义重试策略
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3); // 最大重试次数
retryTemplate.setRetryPolicy(retryPolicy);
// 定义重试间隔
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000);
backOffPolicy.setMultiplier(2);
retryTemplate.setBackOffPolicy(backOffPolicy);
return retryTemplate;
}
}
@RestController
@RequestMapping(value = "/retry")
public class RetryController {
@Autowired
private RetryTest retryTest;
@Autowired
private RetryTemplate retryTemplate;
@GetMapping(value = "testAnnotation")
public ApiResult test() {
retryTest.test();
return ApiUtil.success();
}
@GetMapping(value = "testTemplate")
public ApiResult test1() {
retryTemplate.execute(
retryContext -> {
// 业务逻辑
System.out.println("RetryTest.test() 调用"+ DateUtil.formatDateTime(new Date()));
int i = 1 / 0;
return null;
},
retryContext -> {
System.out.println("RetryTest.test() 重试仍然失败了");
System.out.println(retryContext.getLastThrowable());
return null;
});
return ApiUtil.success();
}
}
日志
RetryTest.test() 调用2023-11-13 22:24:05
RetryTest.test() 调用2023-11-13 22:24:06
RetryTest.test() 调用2023-11-13 22:24:08
RetryTest.test() 重试仍然失败了
java.lang.ArithmeticException: / by zero