1、重试
重试机制在网络服务中非常的重要,由于网路可能存在延迟,网络抖动,网络不稳定的情况。同时在分布式服务中网络的请求的高度密集,有些服务不一定能在规定的时间内完成访问。应该请求服务需要重试几次。以保证服务请求成功
2.springboot 实现retry机制
方式1:普通使用方式(RetryTemplate)
pom.xm文件引入重试框架
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
</dependencies>
普通方式是使用RetryTemplate方式实现,所以我们需要创建RetryTemplate并且将方法放到RetryTemplate中执行需要重试的方法
@Bean
RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
// 设置重试回退操作策略,主要设置重试间隔时间
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000l);
Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
exceptionMap.put(Exception.class, true);
// 设置重试策略,主要设置重试次数
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(100, exceptionMap);
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
return retryTemplate;
}
}
@RestController
@Slf4j
public class RetryController {
private boolean isRetry = false;
@Autowired
RetryTemplate retryTemplate;
@GetMapping("/setparam")
public void setParma(@RequestParam Integer integer, @RequestParam Boolean isRetry) {
this.isRetry = isRetry;
Boolean execute = retryTemplate.execute(
//RetryCallback 重试方法
retryContext -> {
retyrMethod(integer);
return true;
},
retryContext -> {
//RecoveryCallback 达到最大值的方法
log.info("已达到最大重试次数或抛出了不重试的异常~~~");
return false;
}
);
}
public void retyrMethod(Integer integer) {
try {
log.info("isRetry=" + isRetry + ";integer=" + integer);
if (isRetry) {
int ii = 1 / 0;
}
} catch (Exception ex) {
log.error("retyrMethod:" + ex);
throw ex;
}
}
}
- RetryTemplate 承担了重试执行者的角色,它可以设置SimpleRetryPolicy(重试策略,设置重试上限,重试的根源实体),FixedBackOffPolicy(固定的回退策略,设置执行重试回退的时间间隔)。
- RetryTemplate通过execute提交执行操作,需要准备RetryCallback 和RecoveryCallback 两个类实例,前者对应的就是重试回调逻辑实例,包装正常的功能操作,RecoveryCallback实现的是整个执行操作结束的恢复操作实例
- 只有在调用的时候抛出了异常,并且异常是在exceptionMap中配置的异常,才会执行重试操作,否则就调用到excute方法的第二个执行方法RecoveryCallback中
重试策略:
NeverRetryPolicy: 只允许调用RetryCallback一次,不允许重试
AlwaysRetryPolicy: 允许无限重试,直到成功,此方式逻辑不当会导致死循环
SimpleRetryPolicy: 固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略
TimeoutRetryPolicy: 超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试
ExceptionClassifierRetryPolicy: 设置不同异常的重试策略,类似组合重试策略,区别在于这里只区分不同异常的重试
CircuitBreakerRetryPolicy: 有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate
CompositeRetryPolicy: 组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许即可以重试,悲观组合重试策略是指只要有一个策略不允许即可以重试,但不管哪种组合方式,组合中的每一个策略都会执行
重试回退策略:
重试回退策略,指的是每次重试是立即重试还是等待一段时间后重试。默认情况下是立即重试,如果需要配置等待一段时间后重试则需要指定回退策略BackoffRetryPolicy
。
NoBackOffPolicy: 无退避算法策略,每次重试时立即重试
FixedBackOffPolicy: 固定时间的退避策略,需设置参数sleeper和backOffPeriod,sleeper指定等待策略,默认是Thread.sleep,即线程休眠,backOffPeriod指定休眠时间,默认1秒
UniformRandomBackOffPolicy: 随机时间退避策略,需设置sleeper、minBackOffPeriod和maxBackOffPeriod,该策略在minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒
ExponentialBackOffPolicy: 指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier,initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier
ExponentialRandomBackOffPolicy: 随机指数退避策略,引入随机乘数可以实现随机乘数回退
方式2:Spring-Retry注解式(推荐)
spring-retry注解形式依赖于AOP所以需要引入aspect框架
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
1. 首先开启@EnableRetry
表示是否开始重试组件
2. 配置@Retryable
标注此注解的方法在发送异常时会进行重试
属性 | 类型 | 默认值 | 说明 |
interceptor | String | “” | 将interceptor的bean名称应用到retryable(),和其他的属性互斥 |
include | Class[] | {} | 哪些异常可以触发重试 ,默认为空 |
exclude | Class[] | {} | 哪些异常将不会触发重试,默认为空,如果和include属性同时为空,则所有的异常都将会触发重试 |
value | Class[] | {} | 可重试的异常类型 |
label | String | “” | 统计报告的唯—标签。如果没有提供,调用者可以选择忽略它,或者提供默认值 |
maxAttempts | int | 3 | 尝试的最大次数(包括第一次失败),默认为3次 |
backoff | @Backoff | @Backoff() | @Backoff @Backoff()指定用于重试此操作的backoff属性。默认为 |
3.配置重试时间@Backoff
属性 | 类型 | 默认值 | 说明 |
delay | long | 0 | 如果不设置则默认使用1000 ms等待重试,和value同义词 |
maxDelay | long | 0 | 最大重试等待时间 |
multiplier | long | 0 | 用于计算下一个延迟延迟的乘数(大于0生效) |
random | boolean | FALSE | 随机重试等待时间 |
4. @Recover
作为恢复处理程序的方法调用的注释。重试方法最终会调用标注了@Recover 的方法。
@Recover声明的方法和@Retryable 方法相同类型的返回值,Throwable 第一个参数是可选的(但是,如果没有其他参数匹配,则不带该参数的方法将被调用)。从失败方法的参数列表中依次填充后续参数。
@RestController
@Slf4j
public class Retry_ZJ_Controller {
private boolean isRetry = false;
@Autowired
ApplicationContext applicationContext;
@GetMapping("/setparam")
public void setParma(@RequestParam Integer integer, @RequestParam Boolean isRetry) {
this.isRetry = isRetry;
Retry_ZJ_Controller controller = applicationContext.getBean(Retry_ZJ_Controller.class);
controller.retyrMethod(integer);
}
@Retryable(maxAttempts = 4, backoff = @Backoff(delay = 2000l))
public void retyrMethod(Integer integer) {
try {
log.info("isRetry=" + isRetry + ";integer=" + integer);
if (isRetry) {
int ii = 1 / 0;
}
} catch (Exception ex) {
log.error("retyrMethod:" + ex);
throw ex;
}
}
@Recover
public void retryComplete(Exception e) {
log.error("达到最大重试次数,或抛出了一个没有指定进行重试的异常:");
}
}
注意:
1.由于是基于AOP实现,所以不支持类里自调用方法
2.如果重试失败需要给@Recover注解的方法做后续处理,那这个重试的方法不能有返回值,只能是void
3.方法内不能使用try catch,只能往外抛异常
4.@Recover注解来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中),此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。
3.监听重试过程
- 通过实现RetryListener接口,重写
open、close、onError
这三个方法,既可以完成对重试过程的追踪,也可以添加额外的处理逻辑; - 通过继承RetryListenerSupport,也可以从
open、close、onError
这三个方法中,选择性的重写