Spring Retry

重试

重试是指当一个程序运行过程中,遇到网络延迟、中断等情况时,为了保证程序容错性可用性一致性等一个措施。

简介

Spring Retry 是一套 Spring 实现的一套重试机制,主要功能点在于重试熔断。它被广泛用于Spring Batch,Spring Integration, Spring for Apache Hadoop 等项目。它主要是针对可能抛异常的一些调用操作,进行有策略的重试

Spring Retry 提供了 注解编程 的两种支持,提供了 RetryTemplate 的支持

图一

使用

Spring Retry 编程式

导入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RemoteAccessException extends RuntimeException {

public RemoteAccessException(String message) {
super(message);
}

public RemoteAccessException(String message, Throwable cause) {
super(message, cause);
}

public RemoteAccessException(Throwable cause) {
super(cause);
}
}

任务方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class RetryTask {

private static final Logger logger =
LoggerFactory.getLogger(RetryTask.class);

/**
* 重试方法
* @return
*/
public static boolean task() {
logger.info("开启重试任务");
Random random = new Random();
switch (random.nextInt(4)) {
case 0:
logger.info("抛出异常");
throw new IllegalArgumentException("参数异常");
case 1:
logger.info("正确返回");
return true;
case 2:
logger.info("错误返回");
return false;
default:
logger.info("抛出自定义异常");
throw new RemoteAccessException("远程访问异常");
}
}
}

使用 SpringRetryTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDemoRetryTest {

private Logger logger = LoggerFactory.getLogger(SpringDemoRetryTest.class);

/**
* 重试间隔时间(单位:ms)
*/
private Long retryInterval = 1000L;

/**
* 最大重试次数
*/
private int maxRetryTimes = 3;

@Test
public void test() throws Throwable {

// 构建重试模版实例
RetryTemplate retryTemplate = new RetryTemplate();

// 可重试的异常映射 表示哪些异常需要重试 key表示异常的字节码 value为true表示需要重试
Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
retryableExceptions.put(RemoteAccessException.class, true);

// 设置重试策略 设置最大重试次数和可重试的异常映射
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(maxRetryTimes, retryableExceptions);

// 设置重试回退策略
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(retryInterval);

// 绑定重试策略和重试回退策略
retryTemplate.setRetryPolicy(simpleRetryPolicy);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

Boolean execute = retryTemplate.execute(
// RetryCallback
context -> {
boolean task = RetryTask.task();
logger.info("调用的结果:{}", task);
return task;
},
// RecoveryCallback
context -> {
logger.info("已经达到最大重试次数或者抛出不重试的异常");
return false;
}
);
logger.info("执行结果:{}", execute);
}
}

RetryTemplate 承担了重试执行者的角色,它可以设置RetryPolicy(重试策略),BackOffPolicy(固定的回退策略)

RetryTemplate通过execute提交执行操作,需要准备RetryCallbackBackOffPolicy 两个类实例

RetryCallback重试回调逻辑实例,包装正常的功能操作,RecoveryCallback实现的是整个执行操作结束的恢复操作实例

只有在调用的时候抛出了异常,并且异常是在exceptionMap中配置的异常,才会执行重试操作,否则就调用到excute方法的第二个执行方法RecoveryCallback

重试策略

image-20230618002750195

image-20230618002940390

重试回退策略

image-20230618003033222

image-20230618003203978

Spring Retry 声明式

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>

开启注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableRetry
public class SpringDemoRetry {
public static void main(String[] args) {
SpringApplication.run(SpringDemoRetry.class, args);
}
}

添加注解

我们只要在需要重试的方法上加@Retryable,在重试失败的回调方法上加@Recover

图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface SpringRetryService {

/**
* 重试方法
*
* @return
*/
boolean call();

/**
* 重试回退方法
*
* @param e
* @return
*/
boolean recover(Exception e);
}

@Service
public class SpringRetryServiceImpl implements SpringRetryService {

private static final Logger logger = LoggerFactory.getLogger(SpringRetryServiceImpl.class);

@Override
@Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public boolean call() {
return RetryTask.task();
}

@Override
@Recover
public boolean recover(Exception e) {
logger.error("达到最大重试次数,或抛出了一个没有指定进行重试的异常:", e);
return false;
}
}

@Retryable 表示 RemoteAccessException 的异常才进行重试,@Backoff(delay = 2000L, multiplier = 2)) 表示第一次间隔2秒,以后都是次数的2倍,也就是第二次4秒,第三次6秒

调用测试类

1
2
3
4
5
6
@Test
public void test2() {

boolean call = springRetryService.call();
logger.info("--结果:{}--", call);
}

[https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247556669&idx=2&sn=a41ac98ab32cb482588b9059773b54fb&chksm=fa4a618ccd3de89a63cb99c44077302da698ec6c9ac0870783cd12df62002416d522a5267e42&mpshare=1&scene=24&srcid=0617EnhGZStTL20lp7GJvG2x&sharer_sharetime=1687010876772&sharer_shareid=470e895f697284625270102fdb3d1bae#rd]: “重试框架 Spring-Retry 和 Guava-Retry,你知道该怎么选吗?”
[https://juejin.cn/post/7234107489390116925]: “spring-retry详解”