在很多场景中,我们需要“重试”,重试意味着反复执行一段代码直至成功,或者重试多次无果后标记失败。比如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
策略为例
|
|
|
|
|
|
|
|