在很多场景中,我们需要“重试”,重试意味着反复执行一段代码直至成功,或者重试多次无果后标记失败。比如MQ发送消息失败,会采取重试手段,比如工程中使用RPC请求外部服务,可能因为网络波动出现超时而采取重试手段等等.
MQ自身也有重试机制,但是这种机制不是很灵活,如果某些功能没有使用MQ的话,那么就不是那么方便了.
框架概览
Spring Retry 框架广泛使用于Spring Batch,Spring Integration,Spring for Apache Hadoop等spring项目
如下图所示:

RetryTemplate,重试模板,是进入spring-retry框架的整体流程入口RetryCallback,重试回调,用户包装业务流,第一次执行和产生重试执行都会调用这个callback代码RetryPolicy,重试策略,不同策略有不同的重试方式BackOffPolicy,两次重试之间的回避策略,一般采用超时时间机制RecoveryCallback,当所有重试都失败后,回调该接口,提供给业务重试回复机制RetryContext,每次重试都会将其作为参数传入RetryCallback中使用RetryListener,监听重试行为,主要用于监控。
示例
|
|
重试策略:RetryPolicy
重试策略定义了当操作失败时如何进行重试操作

NeverRetryPolicy:只调用RetryCallback一次,不重试AlwaysRetryPolicy:无限重试,最好不要用SimpleRetryPolicy:重试n次,默认3,也是模板默认的策略TimeoutRetryPolicy:在n毫秒内不断进行重试,超过这个时间后停止重试ExceptionClassifierRetryPolicy: 可以根据不同的异常,执行不同的重试策略
回退策略:BackOffPolicy(重试间隔)
当操作执行失败时,根据设置的重试策略进行重试。通过BackoffPolicy可以设定再次重试的时间间隔。

NoBackOffPolicy:不回避FixedBackOffPolicy:n毫秒退避后再进行重试
有状态重试 OR 无状态重试
所谓无状态重试是指重试在一个线程上下文中完成的重试,反之不在一个线程上下文完成重试的就是有状态重试。之前的SimpleRetryPolicy就属于无状态重试,因为重试是在一个循环中完成的。什么时候后会出现或者说需要有状态重试呢?通常有两种情况:事务回滚和熔断。
使用注解
每次有重试需求的时候都写一个RetryTemplate太臃肿了,使用注解可以大大简化开发,减少重复代码。
|
|
@EnableRetry:能否重试,proxyTargetClass属性为true时(默认false),使用CGLIB代理。默认使用标准JAVA注解。当类中有@Retryable注释的方法时,对该方法生成代理。@Retryable:注解需要被重试的方法include指定处理的异常类。默认为空exclude指定不需要处理的异常。默认为空vaue指定要重试的异常。默认为空maxAttempts最大重试次数。默认3次backoff重试等待策略。默认使用@Backoff注解
@Backoff:重试回退策略(立即重试还是等待一会再重试)- 不设置参数时,默认使用
FixedBackOffPolicy,重试等待1000ms - 只设置
delay属性时,使用FixedBackOffPolicy,重试等待指定的毫秒数 - 当设置
delay和maxDealy属性时,重试等待在这两个值之间均态分布 - 使用
delay,maxDealy和multiplier属性时,使用ExponentialBackOffPolicy - 当设置
multiplier属性不等于0时,同时也设置了random属性时,使用ExponentialRandomBackOffPolicy` @Recover: 用于@Retryable失败时的“兜底”处理方法。@Recover注释的方法参数为@Retryable异常类,返回值应与重试方法返回相同,否则无法识别!因此可以针对可能异常设置多个@Recover方法进行“兜底”处理。
- 不设置参数时,默认使用
源码分析
RetryTemplate的execute 是线程安全的,实现逻辑使用ThreadLocal保存每个执行实例的RetryContext执行上下文。
这里以SimpleRetryPolicy和FixedBackOffPolicy策略为例
|
|
|
|
|
|
|
|