重试
重试
为了使处理更健壮且不易失败,有时它会有所帮助
自动重试失败的作,以防后续尝试成功。
容易发生间歇性故障的错误本质上通常是暂时性的。
示例包括对 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
.例如,一个
异常类型在失败前可以比另一个类型重试更多次,方法是将其映射到
不同的政策。
用户可能需要实施自己的重试策略,以做出更多自定义决策。为 例如,当存在已知的、特定于解决方案的 将异常分类为可重试和不可重试。
退避策略
在瞬态故障后重试时,通常稍等片刻再重试会有所帮助,
因为通常故障是由一些只能通过
等待。如果RetryCallback
fails,则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
进入拦截器。