Spring Retry Readme

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chen517611641/article/details/88395836


本项目为Spring应用提供声明式的重试支持。其已经用在Spring Batch,Spring Integration,Spring for Apache Haddop等项目中。
Spring Retry项目脱胎于Spring Batch项目;
此文档虽然是Spring Retry官方的最新文档,但还是落后于代码的更新速度,大家可以在此文档的基础上通过查看Spring Retry项目的源代码以了解更新;

快速开始

@Configuration
@EnableRetry
public class Application {

    @Bean
    public Service service() {
        return new Service();
    }

}

@Service
class Service {
    @Retryable(RemoteAccessException.class)
    public void service() {
        // ... do something
    }
    @Recover
    public void recover(RemoteAccessException e) {
       // ... panic
    }
}

调用service方法时,如果执行失败并抛出一个RemoteAccessException异常,将会重新尝试再次调用,直到达到@Retryable注解中maxAttempts字段默认的3次;如果达到最大的执行次数,还没有执行成功,将执行recover方法;在@Retryable注解中有着丰富的属性,比如value,include,exclude用于配置触发重试的异常的集合;maxAttempts和maxAttemptsExpression用于配置最大重试次数;backoff用于配置回退策略等;

构建

需要Java1.7 和 Maven3.0.5或者更高

$ mvn install

功能和API

RetryTemplate

为了使应用更加的健壮,在一个操作失败的情况下,如果在后续的逻辑中再次重试,可能会成功的这种情况下,自动重试一个失败的操作将是非常有帮助的; XXX。比如,调用一个远程的web service或者RMI service时,因为网络故障而失败或者在数据库更新时出现DeadLockLoserException,这两种情况下,只要稍等片刻,他们可能就会自己修复。Spring Retry提供了RetryOperations来自动重试这些失败的操作。RetryOperations接口的定义如下:

public interface RetryOperations {

    <T> T execute(RetryCallback<T> retryCallback) throws Exception;

    <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback)
        throws Exception;

    <T> T execute(RetryCallback<T> retryCallback, RetryState retryState)
        throws Exception, ExhaustedRetryException;

    <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
        RetryState retryState) throws Exception;

}

RetryCallback回调接口允许开发者定义需要被重试的业务逻辑:

public interface RetryCallback<T> {

    T doWithRetry(RetryContext context) throws Throwable;

}

RetryCallback里的逻辑如果执行失败(也就是抛出一个异常),它将会被重试,直到执行成功(执行过程中没有异常抛出)或者因为达到最大重试次数等被中止执行;在RetryOperations接口的实现中,需要很多逻辑来处理各种的用例,比如当所有的重试次数被耗尽之后,需要recover;还要处理retry state,以允许在两次调用之间共享retry state信息;
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 service调用,然后返回result给用户。如果调用失败,RetryTemplate 将会自动重试指导超过TimeoutRetryPolicy 指定的Timeout的时间;

RetryContext

RetryCallback 回调方法doWithRetry的参数是一个RetryContext对象。大多数情况下,我们的RetryCallback 实现可能都会忽略掉这个参数,但是如果你想在多次重试的期间共享数据,RetryContext可以作为一个attribute 容器,因为RetryContext实现了Spring Core AttributeAccessor接口,实现此功能将会异常方便。

在程序执行的过程中,在同一个线程中,如果重试发生嵌套,则内部的RetryContext将会持有外部的RetryContext引用作为parent;如果在执行重试调用的过程中,需要在内外两个RetryContext里共享数据,这个parent的引用将会非常有用。

RecoveryCallback

当重试次数耗尽之后,RetryOperations将会回调RecoveryCallback接口实现里的逻辑。只要将RecoveryCallback的实现与RetryCallback的实现一起传给RetryOperations,你就能拥有这个能力:

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
    }
});

如果业务逻辑在RetryTemplate决定中止重试(比如达到最大重试次数或者达到配置的超时时间)之前仍然没有成功,Spring Retry将会调用RecoveryCallback里的逻辑,以补偿失败的业务逻辑。

Stateless Retry

最简单的情况,一次重试,仅仅是while的一次loop:RetryTemplate仅仅是持续尝试执行,期间每一次重试要么成功,要么失败。RetryContext包含一些状态信息,以用于确定是继续重试还是中止重试,但是这些状态信息仅仅是在栈中,没有必要全局保存,这种情况,我们称之为无状态重试(stateless retry)。无状态重试和有状态重试的区别在RetryPolicy的实现有体现,RetryTemplate这两种类型的重试都能处理。在无状态的重试中,RetryCallback和RecoveryCallback的调用总是与执行重试的逻辑在一个线程中。

Stateful Retry

在应用执行失败时导致一个事务性的资源变得无效的情况下,有一些需要特别考虑的情况;那个远程调用的例子不属于这种情况,因为没有牵扯到事务性的资源;如果是一次数据库更新,特别是在使用Hibernate的时候,就符合现在所说的情况。在这种情况下,在重试的期间,如果调用失败,重新抛出异常,以让事务接收到异常然后回滚将会变得非常有意义,最后我们可以重新开启一个有效的事务。

在这些情况下,无状态重试将有些力不从心。因为重新抛出异常然后回滚事务牵扯到leaving the RetryOperations.execute() method and 丢失栈中的context。为了避免丢失context,我们必须引入存储策略,将context从栈中放到堆中存储。Spring Retry提供了RetryContextCache来实现此目的。RetryTemplate持有一个RetryContextCache的引用;RetryContextCache默认的实现是MapRetryContextCache,其使用一个简单的Map对象,从而将context保存在内存中。MapRetryContextCache必须被严格的控制最大值,避免内存溢出,但是他没有任何高级的缓存功能,比如说TTL(time to live),如果你需要这些特性,可以考虑给MapRetryContextCache注入一个有此特性的Map对象。

当一个失败的操作,重新执行时(通常是被包含在一个新的事务中),RetryOperations能够识别出这是执行失败过的操作。Spring Retry提供了RetryState来实现此功能;RetryOperations提供了特定的方法来支持RetryState实现此功能。

失败的操作可以被识别出来是重试的操作的方法是,RetryState可以穿过在多次重试调用。为了识别出这个状态,开发者可以提供一个RetryState对象,来负责返回一个唯一的key来区分。这个key同时作为存储在RetryContextCache中的context的key。

注意RetryState#getKey返回的对象的equals()方法和hashCode()方法的实现,最好的方式是使用business key来识别;比如,在处理JMS消息的时候,可以使用消息的ID。

Retry Policies

在RetryTemplate中,在各个execute系列方法中,继续重试还是中止重试是委托给RetryPolicy决策的。RetryPolicy同时也是一个RetryContext的工厂(RetryPolicy#open方法新创建一个RetryContext并返回)。RetryTemplate负责使用其持有的RetryPolicy对象来创建一个RetryContext对象,然后在每次attempt的时候将其传递给RetryCallback对象;

一次RetryCallback的调用失败之后,RetryTemplate必须调用RetryPolicy#registerThrowable方法来更新存储在RetryContext的RetryState对象,然后调用RetryPolicy#canRetry方法以判断是否进行下一次重试。如果不能继续重试(比如达到最大重试次数或者检测到超时),然后,RetryPolicy负责识别出exhausted state,但是不负责处理异常。RetryTemplate将会把原始异常对象抛出,

猜你喜欢

转载自blog.csdn.net/chen517611641/article/details/88395836