重试
重试
为了使处理更健壮且不易失败,有时它会有所帮助
自动重试失败的作,以防后续尝试成功。
容易发生间歇性故障的错误本质上通常是暂时性的。
示例包括对 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;
}
基本回调是一个简单的接口,可让您插入一些业务逻辑 重试,如以下接口定义所示:
public interface RetryCallback<T, E extends Throwable> {
T doWithRetry(RetryContext context) throws E;
}
回调运行,如果它失败(通过抛出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 参数RetryCallback是一个RetryContext.许多回调忽略
上下文,但如有必要,它可以用作属性包来存储
迭代的持续时间。
一个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
}
});
如果业务逻辑在模板决定中止之前没有成功,则 客户端有机会通过恢复回调执行一些替代处理。
无状态重试
在最简单的情况下,重试只是一个 while 循环。这RetryTemplate可以保持
尝试直到成功或失败。这RetryContext包含一些状态
确定是重试还是中止,但此状态在堆栈上,无需
将其存储在全局的任何位置,因此我们称之为无状态重试。之间的区别
无状态和有状态重试包含在RetryPolicy(这RetryTemplate可以处理两者)。在无状态重试中,重试回调始终是
在失败时在同一线程中执行。
有状态重试
如果故障导致事务资源无效,则有一些 特殊注意事项。这不适用于简单的远程呼叫,因为没有 事务资源(通常),但它有时确实适用于数据库更新, 尤其是在使用 Hibernate 时。在这种情况下,只有重新抛出 异常,该异常立即调用了失败,以便事务可以回滚和 我们可以开始一个新的、有效的交易。
在涉及事务的情况下,无状态重试是不够好的,因为
重新抛出和回滚必然涉及离开RetryOperations.execute()方法
并可能丢失堆栈上的上下文。为了避免丢失它,我们必须
引入一种存储策略,将其从堆栈中取出并(至少)放在堆中
存储。为此,Spring Batch提供了一种名为RetryContextCache,可以注入RetryTemplate.默认值
实现RetryContextCache在内存中,使用简单的Map.高深
在群集环境中与多个进程一起使用也可以考虑实现
这RetryContextCache使用某种集群缓存(但是,即使在集群
环境,这可能有点矫枉过正)。
部分责任RetryOperations就是识别失败的作
当它们在新的执行中返回时(通常包装在新事务中)。自
为了实现这一点,Spring Batch 提供了RetryState抽象化。这适用于
与特殊的execute方法RetryOperations接口。
识别失败作的方式是通过识别多个
调用重试。若要标识状态,用户可以提供RetryState负责返回标识项目的唯一键的对象。标识符
用作RetryContextCache接口。
|
在实现时要非常小心 |
当重试用尽时,还可以选择在
不同的方式,而不是调用RetryCallback(现在认为这是可能的
失败)。就像在无状态情况下一样,此选项由RecoveryCallback,可以通过将其传递给execute方法RetryOperations.
重试与否的决定实际上委托给了常规RetryPolicy,因此
通常可以在那里注入对限制和超时的担忧(本文稍后将介绍
章节)。
重试策略
在一个RetryTemplate,决定重试或失败execute方法是
由RetryPolicy,这也是一家工厂RetryContext.这RetryTemplate有责任使用当前策略创建RetryContext并将其传递给RetryCallback在每一次尝试中。回调后
fails,则RetryTemplate必须调用RetryPolicy要求它更新其
状态(存储在RetryContext),然后询问策略是否再次尝试
可以制作。如果无法进行另一次尝试(例如,当达到限制或
超时),则策略还负责处理耗尽状态。
简单实现抛出RetryExhaustedException,这会导致任何封闭
要回滚的事务。更复杂的实现可能会尝试将
一些恢复作,在这种情况下,事务可以保持不变。
|
失败本质上是可重试的,也可以不可重试。如果相同的异常总是将 被抛出业务逻辑,重试是没有好处的。所以不要在所有 异常类型。相反,尽量只关注那些你期望的例外情况 可重试。更积极地重试通常对业务逻辑没有害处,但是 这是浪费,因为如果失败是确定性的,你就会花时间重试某些东西 你提前知道是致命的。 |
Spring Batch 提供了一些简单的无状态通用实现RetryPolicy如SimpleRetryPolicy和TimeoutRetryPolicy(在前面的示例中使用)。
这SimpleRetryPolicy允许对异常类型的任何命名列表重试,最多
固定次数。它还列出了永远不应该的“致命”例外
retryed,并且此列表将覆盖可重试列表,以便它可以用于提供更精细的
控制重试行为,如以下示例所示:
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
}
});
还有一种更灵活的实现,称为ExceptionClassifierRetryPolicy,
它允许用户为一组任意异常配置不同的重试行为
类型通过ExceptionClassifier抽象化。该策略的工作原理是调用
将异常转换为委托的分类器RetryPolicy.例如,一个
异常类型在失败前可以比另一个类型重试更多次,方法是将其映射到
不同的政策。
用户可能需要实施自己的重试策略,以做出更多自定义决策。为 例如,当存在已知的、特定于解决方案的 将异常分类为可重试和不可重试。
退避策略
在瞬态故障后重试时,通常稍等片刻再重试会有所帮助,
因为通常故障是由一些只能通过
等待。如果RetryCallbackfails,则RetryTemplate可以根据
这BackoffPolicy.
以下代码显示了BackOffPolicy接口:
public interface BackoffPolicy {
BackOffContext start(RetryContext context);
void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException;
}
一个BackoffPolicy可以自由地以它选择的任何方式实现 backOff。政策
由 Spring Batch 提供开箱即用Object.wait().一个常见的用例是
backoff,等待期呈指数级增加,以避免两次重试进入
锁定步骤,两者都失败(这是从以太网中吸取的教训)。为此,
Spring Batch 提供了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);
}
这open和close回调在整个重试之前和之后出现,最简单
case 和onError适用于个人RetryCallback调用。这close方法
可能还会收到Throwable.如果出现错误,则是最后一个抛出的错误
这RetryCallback.
请注意,当有多个侦听器时,它们位于一个列表中,因此有一个顺序。在这种情况下,open以相同的顺序调用,而onError和close在相反的顺序中调用。
声明式重试
有时,有些业务处理您知道每次都想重试
发生。这方面的典型例子是远程服务调用。Spring Batch 提供了一个
AOP 拦截器,将方法调用包装在RetryOperations实现只是
这个目的。这RetryOperationsInterceptor执行截获的方法并重试
根据RetryPolicy在提供的RepeatTemplate.
以下示例显示了一个声明式重试,它使用 Spring AOP 命名空间来
重试对名为remoteCall(有关如何配置的更多详细信息
AOP 拦截器,请参阅 Spring 用户指南):
<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 配置重试
服务调用调用名为remoteCall(有关如何配置 AOP 的更多详细信息
拦截器,请参阅 Spring 用户指南):
@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;
}
前面的示例使用默认的RetryTemplate拦截器内部。要更改
策略或侦听器,您可以注入RetryTemplate进入拦截器。