重试
重试
为了使处理更健壮且不易出错,有时有助于
自动重试失败的操作,以防它在后续尝试中可能成功。
易受间歇性故障影响的错误在本质上通常是暂时性的。
示例包括对 Web 服务的远程调用,该服务由于网络故障或数据库更新而失败。DeadlockLoserDataAccessException
RetryTemplate
从 2.2.0 开始,重试功能已从 Spring Batch 中抽出。 它现在是新库 Spring Retry 的一部分。 |
为了自动化重试操作,Spring Batch 具有该策略。这
以下接口定义 :RetryOperations
RetryOperations
public interface RetryOperations {
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback)
throws E;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState)
throws E, ExhaustedRetryException;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback,
RetryState retryState) throws E;
}
基本回调是一个简单的接口,允许您插入一些业务逻辑 retried,如以下接口定义所示:
public interface RetryCallback<T, E extends Throwable> {
T doWithRetry(RetryContext context) throws E;
}
回调运行,如果失败(通过抛出 ),则重试,直到
要么成功,要么实现中止。接口中有许多重载的方法。这些方法处理各种用途
当所有重试尝试都用尽时进行恢复的情况,并处理重试状态,该
允许客户端和实现在调用之间存储信息(我们将在 More 中介绍这一点
详情请见本章)。Exception
execute
RetryOperations
最简单的通用实现是 。它
可以按如下方式使用:RetryOperations
RetryTemplate
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
Foo result = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return result;
}
});
在前面的示例中,我们进行 Web 服务调用并将结果返回给用户。如果 该调用失败,然后重试,直到达到超时。
RetryContext
的 method 参数是一个 .许多回调会忽略
上下文,但如有必要,它可以用作属性包来存储
iteration 的持续时间。RetryCallback
RetryContext
如果 A 中正在进行嵌套重试,则 A 具有父上下文
线。父上下文有时可用于存储需要共享的数据
在对 的调用之间。RetryContext
execute
RecoveryCallback
当重试用尽时,可以将控制权传递给不同的回调
称为 .要使用此功能,客户端需要一起传入回调
添加到相同的方法,如以下示例所示:RetryOperations
RecoveryCallback
Foo foo = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// business logic here
},
new RecoveryCallback<Foo>() {
Foo recover(RetryContext context) throws Exception {
// recover logic here
}
});
如果在模板决定中止之前业务逻辑未成功,则 客户端有机会通过 Recovery 回调进行一些替代处理。
无状态重试
在最简单的情况下,重试只是一个 while 循环。可以保持
尝试,直到成功或失败。contains some state 到
确定是重试还是中止,但此状态在堆栈上,没有必要
将其存储在全球任何位置,因此我们将其称为 Stateless Retry。之间的区别
无状态和有状态重试包含在 (可以同时处理两者) 的实现中。在无状态重试中,重试回调始终为
在失败时所在的同一线程中执行。RetryTemplate
RetryContext
RetryPolicy
RetryTemplate
状态重试
如果失败导致事务性资源变得无效,则有一些 特殊注意事项。这不适用于简单的远程调用,因为没有 transactional 资源(通常),但有时它确实适用于数据库更新, 尤其是在使用 Hibernate 时。在这种情况下,只有重新抛出 Exception 立即调用失败,以便事务可以回滚,并且 我们可以开始一个新的、有效的交易。
在涉及事务的情况下,无状态重试还不够好,因为
re-throw 和 roll back 必然涉及离开
并可能丢失堆栈上的上下文。为了避免丢失它,我们必须
引入一种存储策略,将其从堆栈中取出并(至少)放入堆中
存储。为此, Spring Batch 提供了一个名为 的存储策略,该策略可以注入到 .默认的
在内存中实现 IS,使用简单的 .高深
在集群环境中与多个进程一起使用时,也可以考虑实现
具有某种集群缓存(但是,即使在集群
环境,这可能有点矫枉过正)。RetryOperations.execute()
RetryContextCache
RetryTemplate
RetryContextCache
Map
RetryContextCache
部分责任是识别失败的操作
当它们在新的执行中返回时(并且通常包装在新事务中)。自
为了促进这一点,Spring Batch 提供了抽象。这在
与接口中的特殊方法结合使用。RetryOperations
RetryState
execute
RetryOperations
识别失败操作的方法是识别多个
重试的调用。要识别状态,用户可以提供一个对象,该对象负责返回标识项目的唯一键。标识符
在界面中用作键。RetryState
RetryContextCache
在 和 的实现时要非常小心
返回的键。最好的建议是使用 business key 来识别
项目。对于 JMS 消息,可以使用消息 ID。 |
当重试用尽时,还可以选择在
以不同的方式调用(现在假定这很可能是
失败)。就像在无状态情况下一样,此选项由 提供,可以通过将其传递给 的方法 来提供。RetryCallback
RecoveryCallback
execute
RetryOperations
是否重试的决定实际上委托给常规 ,因此
通常对 limits 和 timeout 的担忧可以注入到那里(稍后将对此进行描述
章节)。RetryPolicy
重试策略
在 中,在方法中重试或失败的决定是
由 确定,它也是 的工厂。它有责任使用当前策略来创建 ,并在每次尝试时将其传递给 。回调后
失败时,必须调用 以请求它更新其
state(存储在 中),然后询问策略是否再次尝试
可以制作。如果无法进行其他尝试(例如,当达到限制或
timeout),则策略还负责处理 exhausted 状态。
简单实现 throw ,这会导致任何封闭
要回滚的事务。更复杂的 implementations 可能会尝试采用
some recovery 操作,在这种情况下,事务可以保持不变。RetryTemplate
execute
RetryPolicy
RetryContext
RetryTemplate
RetryContext
RetryCallback
RetryTemplate
RetryPolicy
RetryContext
RetryExhaustedException
失败本质上是可重试的,也可能是不可重试的。如果相同的异常总是 从业务逻辑中抛出,重试它没有好处。所以不要对所有 异常类型。相反,请尝试只关注您预期的那些异常 retryable 的。更积极地重试通常不会对业务逻辑有害,但是 这是浪费的,因为如果失败是确定性的,你会花时间重试某些事情 你提前知道是致命的。 |
Spring Batch 提供了一些简单的 stateless 通用实现,例如 和 (在前面的示例中使用)。RetryPolicy
SimpleRetryPolicy
TimeoutRetryPolicy
它允许对任何命名的异常类型列表进行重试,最大为
固定次数。它还列出了永远不应该出现的 “致命” 异常
retried,并且此列表会覆盖 retryable 列表,以便它可以用于提供更精细的
控制重试行为,如以下示例所示:SimpleRetryPolicy
SimpleRetryPolicy policy = new SimpleRetryPolicy();
// Set the max retry attempts
policy.setMaxAttempts(5);
// Retry on all exceptions (this is the default)
policy.setRetryableExceptions(new Class[] {Exception.class});
// ... but never retry IllegalStateException
policy.setFatalExceptions(new Class[] {IllegalStateException.class});
// Use the policy...
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(policy);
template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// business logic here
}
});
还有一个更灵活的实现,称为 ,
这允许用户为任意一组异常配置不同的重试行为
类型。该策略的工作原理是调用
classifier 将异常转换为 delegate 。例如,一个
异常类型在失败之前可以比另一个类型重试更多次,方法是将其映射到
不同的策略。ExceptionClassifierRetryPolicy
ExceptionClassifier
RetryPolicy
用户可能需要实施自己的重试策略,以便做出自定义程度更高的决策。为 实例,当存在众所周知的特定于解决方案的 将异常分类为 Retryable 和 Not Retryable。
退避策略
在暂时性故障后重试时,通常片刻后再尝试会有所帮助。
因为通常失败是由一些问题引起的,而这些问题只能通过以下方式解决
等待。如果 a 失败,则可以根据
这。RetryCallback
RetryTemplate
BackoffPolicy
以下代码显示了接口的接口定义:BackOffPolicy
public interface BackoffPolicy {
BackOffContext start(RetryContext context);
void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException;
}
A 可以自由地以它选择的任何方式实现 backOff。策略
由 Spring Batch 提供的开箱即用的 all use .一个常见的用例是
backoff 的等待时间呈指数级增加,以避免两次重试进入
lock step 和 both failed(这是从以太网中吸取的教训)。为此,
Spring Batch 提供了 .BackoffPolicy
Object.wait()
ExponentialBackoffPolicy
听众
通常,能够接收跨切关注点的其他回调是很有用的
在许多不同的重试中。为此,Spring Batch 提供了接口。允许用户注册 和
在
迭 代。RetryListener
RetryTemplate
RetryListeners
RetryContext
Throwable
以下代码显示了 的接口定义:RetryListener
public interface RetryListener {
<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
}
和 回调以最简单的方式出现在整个重试之前和之后
case 的 intent 和 apply 的 single 调用。方法
可能还会收到 .如果出现错误,则它是
这。open
close
onError
RetryCallback
close
Throwable
RetryCallback
请注意,当有多个侦听器时,它们位于一个列表中,因此有一个 order。
在这种情况下,以相同的顺序调用 while 和 在
倒序。open
onError
close
声明式重试
有时,您知道每次都要重试一些业务处理
发生。这方面的经典示例是远程服务调用。Spring Batch 提供了一个
AOP 拦截器,它将方法调用包装在实现中,仅用于
这个目的。执行拦截的方法并重试
根据中提供的 .RetryOperations
RetryOperationsInterceptor
RetryPolicy
RepeatTemplate
下面的示例展示了一个声明式重试,它使用 Spring AOP 名称空间来
重试对调用的方法的服务调用(有关如何配置
AOP 拦截器,请参见 Spring 用户指南):remoteCall
<aop:config>
<aop:pointcut id="transactional"
expression="execution(* com..*Service.remoteCall(..))" />
<aop:advisor pointcut-ref="transactional"
advice-ref="retryAdvice" order="-1"/>
</aop:config>
<bean id="retryAdvice"
class="org.springframework.retry.interceptor.RetryOperationsInterceptor"/>
以下示例显示了使用 java 配置重试
service 调用调用的方法(有关如何配置 AOP 的更多详细信息
拦截器,请参见 Spring 用户指南):remoteCall
@Bean
public MyService myService() {
ProxyFactory factory = new ProxyFactory(RepeatOperations.class.getClassLoader());
factory.setInterfaces(MyService.class);
factory.setTarget(new MyService());
MyService service = (MyService) factory.getProxy();
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns(".*remoteCall.*");
RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();
((Advised) service).addAdvisor(new DefaultPointcutAdvisor(pointcut, interceptor));
return service;
}
前面的示例在拦截器中使用 default。要将
策略或侦听器,则可以将 的实例注入到拦截器中。RetryTemplate
RetryTemplate