重试
重试
为了使处理更健壮且不易出错,有时有助于
自动重试失败的作,以防它在后续尝试中可能成功。
易受间歇性故障影响的错误在本质上通常是暂时性的。
示例包括对 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;
}
回调运行,如果失败(通过抛出Exception
),它会重试,直到
要么成功,要么实现中止。有许多超载execute
方法中的RetryOperations
接口。这些方法处理各种用途
当所有重试尝试都用尽时进行恢复的情况,并处理重试状态,该
允许客户端和实现在调用之间存储信息(我们将在 More 中介绍这一点
详情请见本章)。
最简单的通用实现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
.许多回调会忽略
上下文,但如有必要,它可以用作属性包来存储
iteration 的持续时间。
一个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 循环。这RetryTemplate
可以保持
尝试,直到成功或失败。这RetryContext
contains some state 到
确定是重试还是中止,但此状态在堆栈上,没有必要
将其存储在全球任何位置,因此我们将其称为 Stateless Retry。之间的区别
无状态和有状态重试包含在RetryPolicy
(RetryTemplate
可以同时处理两者)。在无状态重试中,重试回调始终为
在失败时所在的同一线程中执行。
状态重试
如果失败导致事务性资源变得无效,则有一些 特殊注意事项。这不适用于简单的远程调用,因为没有 transactional 资源(通常),但有时它确实适用于数据库更新, 尤其是在使用 Hibernate 时。在这种情况下,只有重新抛出 Exception 立即调用失败,以便事务可以回滚,并且 我们可以开始一个新的、有效的交易。
在涉及事务的情况下,无状态重试还不够好,因为
re-throw 和 roll back 必然涉及将RetryOperations.execute()
方法
并可能丢失堆栈上的上下文。为了避免丢失它,我们必须
引入一种存储策略,将其从堆栈中取出并(至少)放入堆中
存储。为此,Spring Batch 提供了一个名为RetryContextCache
,可以注入到RetryTemplate
.默认的
实现RetryContextCache
在内存中,使用简单的Map
.高深
在集群环境中与多个进程一起使用时,也可以考虑实现
这RetryContextCache
使用某种集群缓存(但是,即使在集群
环境,这可能有点矫枉过正)。
部分责任RetryOperations
是识别失败的作
当它们在新的执行中返回时(并且通常包装在新事务中)。自
为了实现这一点,Spring Batch 提供了RetryState
抽象化。这在
与特殊execute
方法中的RetryOperations
接口。
识别失败作的方法是识别多个
重试的调用。要识别状态,用户可以提供RetryState
对象,该对象负责返回标识项目的唯一键。标识符
用作RetryContextCache
接口。
在实现 |
当重试用尽时,还可以选择在
不同的方式,而不是调用RetryCallback
(现在推测这很可能
失败)。就像在无状态情况下一样,此选项由RecoveryCallback
,可以通过将其传递给execute
method 的RetryOperations
.
是否重试的决定实际上是委托给常规的RetryPolicy
,因此
通常对 limits 和 timeout 的担忧可以注入到那里(稍后将对此进行描述
章节)。
重试策略
在RetryTemplate
中,在execute
method 为
由RetryPolicy
,它也是RetryContext
.这RetryTemplate
负责使用当前策略创建一个RetryContext
并将其传递给RetryCallback
每一次尝试。回调后
失败时,该RetryTemplate
必须调用RetryPolicy
以要求它更新其
状态(存储在RetryContext
),然后询问策略是否再次尝试
可以制作。如果无法进行其他尝试(例如,当达到限制或
timeout),则策略还负责处理 exhausted 状态。
简单的实现抛出RetryExhaustedException
,这会导致任何封闭
要回滚的事务。更复杂的 implementations 可能会尝试采用
some recovery作,在这种情况下,事务可以保持不变。
失败本质上是可重试的,也可能是不可重试的。如果相同的异常总是 从业务逻辑中抛出,重试它没有好处。所以不要对所有 异常类型。相反,请尝试只关注您预期的那些异常 retryable 的。更积极地重试通常不会对业务逻辑有害,但是 这是浪费的,因为如果失败是确定性的,你会花时间重试某些事情 你提前知道是致命的。 |
Spring Batch 提供了一些简单的无状态通用实现RetryPolicy
如SimpleRetryPolicy
和TimeoutRetryPolicy
(在前面的示例中使用)。
这SimpleRetryPolicy
允许对任何命名的异常类型列表进行重试,最大为
固定次数。它还列出了永远不应该出现的 “致命” 异常
retried,并且此列表会覆盖 retryable 列表,以便它可以用于提供更精细的
控制重试行为,如以下示例所示:
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
抽象化。该策略的工作原理是调用
classifier 将异常转换为委托RetryPolicy
.例如,一个
异常类型在失败之前可以比另一个类型重试更多次,方法是将其映射到
不同的策略。
用户可能需要实施自己的重试策略,以便做出自定义程度更高的决策。为 实例,当存在众所周知的特定于解决方案的 将异常分类为 Retryable 和 Not Retryable。
退避策略
在暂时性故障后重试时,通常片刻后再尝试会有所帮助。
因为通常失败是由一些问题引起的,而这些问题只能通过以下方式解决
等待。如果RetryCallback
失败时,该RetryTemplate
可以根据
这BackoffPolicy
.
以下代码显示了BackOffPolicy
接口:
public interface BackoffPolicy {
BackOffContext start(RetryContext context);
void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException;
}
一个BackoffPolicy
可以自由地以它选择的任何方式实现 backOff。策略
由 Spring Batch 提供,开箱即用,全部使用Object.wait()
.一个常见的用例是
backoff 的等待时间呈指数级增加,以避免两次重试进入
lock step 和 both failed(这是从以太网中吸取的教训)。为此,
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
.
请注意,当有多个侦听器时,它们位于一个列表中,因此有一个 order。
在这种情况下,open
以相同的顺序调用,而onError
和close
被调用
倒序。
声明式重试
有时,您知道每次都要重试一些业务处理
发生。这方面的经典示例是远程服务调用。Spring Batch 提供了一个
AOP 拦截器将方法调用包装在RetryOperations
implementation for just
这个目的。这RetryOperationsInterceptor
执行 intercepted 方法并重试
失败时,根据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 配置重试
service 调用到名为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
在拦截器内部。要将
policies 或侦听器中,您可以注入RetryTemplate
进入拦截器。