## Spring-Retry
**引入Maven依赖**
```xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
```
使用`@EnableRetry`注解开启重试机制
```java
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
```
> **@Retryable说明**
>
> * recover:用于重试失败的兜底策略,一般不使用这个属性
> * interceptor:忽略
> * value:支持重试的异常,与include等价,如果value和include都不配置则重试所有
> * include:需要重试的异常
> * exclude:不重试的异常
> * label:重试处的唯一名词定义,不定义则会基于方法签名生成,一般用不到
> * stateful:是否有状态的重试(需要学习spring-retry 有状态重试相关,一般用不到)
> * maxAttempts:最大执行次数
> * maxAttemptsExpression:忽略
> * backoff:重试策略
> * listeners:spring-retry监听器配置,此处配置的是监听器在ioc容器中的id,需要了解spring retry的监听器才会使用
> **Backoff说明**
>
> * value:与delay效果等价,失败后等待多少ms重试下一次
> * delay:与value效果等价,失败后等待多少ms重试下一次
> * maxDelay:重试等待的最大时间,如果maxDelay<delay,则maxDelay默认为30000ms
> * multiplier:重试时间间隔指数增长倍数
> * delayExpression:延时表达式
> * maxDelayExpression:最大延时表达式
> * multiplierExpression:增长表达式
> * random:附加随机事件,需要multiplier>0
**@Recover**
一个方法如果需要recover策略,只需要在同类中加入一个方法,方法使用recover标记即可
注意:方法的第一个参数为异常类型,后面为原方法的参数
```java
@Recover
public void defaultFallback(Exception e){
//do it
}
```
**测试**
注解:@Retryable
效果:默认重试3次,下次重试延时1s
```
2023-04-13 10:34:36.277 INFO 16704 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request 2...
2023-04-13 10:34:37.289 INFO 16704 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request 2...
2023-04-13 10:34:38.296 INFO 16704 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request 2...
```
注解:@Retryable(maxAttempts = 7,include = {Exception.class},backoff = @Backoff(delay = 1000,maxDelay = 10000))
效果:重试7次,延时在1000ms-10000ms之间
```
2023-04-13 10:36:38.484 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:36:41.342 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:36:44.318 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:36:48.794 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:36:50.264 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:36:59.808 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
2023-04-13 10:37:01.882 INFO 6024 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request3 ...
```
注解:@Retryable(maxAttempts = 5,include = {Exception.class},backoff = @Backoff(delay = 1000,multiplier = 2))
效果:重试5次,第一次延时为1000ms,之后为第一次的2倍
```
2023-04-13 10:39:11.654 INFO 5692 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request4 ...
2023-04-13 10:39:12.665 INFO 5692 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request4 ...
2023-04-13 10:39:14.667 INFO 5692 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request4 ...
2023-04-13 10:39:18.672 INFO 5692 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request4 ...
2023-04-13 10:39:26.673 INFO 5692 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request4 ...
```
注解:@Retryable(maxAttempts = 5,backoff = @Backoff(value = 1500,maxDelay = 10000,multiplier = 3))
效果:最大重试5次,第一次延时1500ms,之后为前一次延时时长3倍,时长大于10000ms时,则按10000ms计算
```
2023-04-13 10:41:43.491 INFO 13928 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request5 ...
2023-04-13 10:41:45.005 INFO 13928 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request5 ...
2023-04-13 10:41:49.510 INFO 13928 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request5 ...
2023-04-13 10:41:59.523 INFO 13928 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request5 ...
2023-04-13 10:42:09.525 INFO 13928 --- [ main] c.v.service.impl.HttpRestServiceImpl : do request5 ...
```
**失效场景**
因为spring-retry采用的时aspectj动态代理,所以也会出现一些类似于spring事务的失效场景
例如
* 在同一service中a方法调用b方法,b方法上标有`@Retryable`注解,那么在运行期间a方法调用b方法时相当于this.b(),而此时的this指向的是原本被代理的对象,所以会导致注解失效
* 在service中主动try-catch方法中的异常,导致aop没有拦截到异常信息,所以也不会进行重试