Guide to Spring Retry – Spring重试指南

最后修改: 2017年 1月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Spring Retry provides an ability to automatically re-invoke a failed operation. This is helpful where the errors may be transient (like a momentary network glitch).

Spring Retry提供了一种自动重启失败操作的能力。这在错误可能是暂时性的(如一时的网络故障)时很有帮助。

In this tutorial, we’ll see the various ways to use Spring Retry: annotations, RetryTemplate, and callbacks.

在本教程中,我们将看到使用Spring Retry的各种方式:注释、RetryTemplate,和回调。

2. Maven Dependencies

2.Maven的依赖性

Let’s begin by adding the spring-retry dependency into our pom.xml file:

让我们首先在pom.xml文件中添加spring-retry依赖性。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

We also need to add Spring AOP into our project:

我们还需要将Spring AOP添加到我们的项目中。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

Have a look at Maven Central for the latest versions of the spring-retry and spring-aspects dependencies.

请到Maven中心查看spring-retryspring-aspects的最新版本依赖。

3. Enabling Spring Retry

3.启用Spring重试

To enable Spring Retry in an application, we need to add the @EnableRetry annotation to our @Configuration class:

要在应用程序中启用Spring Retry,我们需要将@EnableRetry注解添加到我们的@Configuration类中。

@Configuration
@EnableRetry
public class AppConfig { ... }

4. Using Spring Retry

4.使用Spring Retry

4.1. @Retryable Without Recovery

4.1.@Retryable无恢复

We can use the @Retryable annotation to add retry functionality to methods:

我们可以使用@Retryable注解来为方法添加重试功能

@Service
public interface MyService { 

    @Retryable(value = RuntimeException.class) 
    void retryService(String sql); 
}

Here, the retry is attempted when a RuntimeException is thrown.

在这里,当抛出RuntimeException时,会尝试重试

Per @Retryable‘s default behavior, the retry may happen up to three times, with a delay of one second between retries.

根据@Retryable的默认行为,重试最多可以发生三次,重试之间有一秒钟的延迟。

4.2. @Retryable and @Recover

4.2.@Retryable@Recover

Let’s now add a recovery method using the @Recover annotation:

现在让我们使用@Recover注解添加一个恢复方法

@Service
public interface MyService { 

    @Retryable(value = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException; 

    @Recover
    void recover(SQLException e, String sql); 
}

Here, the retry is attempted when an SQLException is thrown. The @Recover annotation defines a separate recovery method when a @Retryable method fails with a specified exception.

这里,当抛出SQLException 时,会尝试重试。 @Recover注解定义了当@Retryable方法因指定的异常而失败时的一个单独恢复方法。

Consequently, if the retryServiceWithRecovery method keeps throwing an SqlException after three attempts, the recover() method will be called.

随后,如果retryServiceWithRecovery方法在三次尝试后不断抛出SqlExceptionrecover()方法将被调用。

The recovery handler should have the first parameter of type Throwable (optional) and the same return type. The following arguments are populated from the argument list of the failed method in the same order.

恢复处理程序应该有Throwable(optional)类型的第一个参数和相同的返回类型。以下参数按相同顺序从失败方法的参数列表中填充。

4.3. Customizing @Retryable’s Behavior

4.3.定制@Retryable的行为

In order to customize a retry’s behavior, we can use the parameters maxAttempts and backoff:

为了定制重试的行为,我们可以使用参数maxAttemptsbackoff

@Service
public interface MyService {

    @Retryable(value = SQLException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
    void retryServiceWithCustomization(String sql) throws SQLException;
}

There will be up to two attempts and a delay of 100 milliseconds.

将有最多两次尝试,并有100毫秒的延迟。

4.4. Using Spring Properties

4.4.使用Spring属性

We can also use properties in the @Retryable annotation.

我们也可以使用@Retryable注解中的属性。

To demonstrate this, we’ll see how to externalize the values of delay and maxAttempts into a properties file.

为了证明这一点,我们将看到如何将delaymaxAttempts的值外化为一个属性文件。

First, let’s define the properties in a file called retryConfig.properties:

首先,让我们在一个名为retryConfig.properties的文件中定义这些属性。

retry.maxAttempts=2
retry.maxDelay=100

We then instruct our @Configuration class to load this file:

然后我们指示我们的@Configuration类来加载这个文件。

// ...
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }

Finally, we can inject the values of retry.maxAttempts and retry.maxDelay in our @Retryable definition:

最后,我们可以在我们的@Retryable定义中注入retry.maxAttemptsretry.maxDelay的值。

@Service 
public interface MyService {

    @Retryable(value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
               backoff = @Backoff(delayExpression = "${retry.maxDelay}")) 
    void retryServiceWithExternalConfiguration(String sql) throws SQLException; 
}

Please note that we are now using maxAttemptsExpression and delayExpression instead of maxAttempts and delay.

请注意,我们现在使用maxAttemptsExpressiondelayExpression来代替maxAttemptsdelay

5. RetryTemplate

5.RetryTemplate

5.1. RetryOperations

5.1.RetryOperations

Spring Retry provides RetryOperations interface, which supplies a set of execute() methods:

Spring Retry提供了RetryOperations接口,它提供了一组execute()方法。

public interface RetryOperations {

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

    ...
}

The RetryCallback, which is a parameter of the execute(), is an interface that allows the insertion of business logic that needs to be retried upon failure:

RetryCallbackexecute()的参数,它是一个接口,允许插入需要在失败时重试的业务逻辑。

public interface RetryCallback<T> {

    T doWithRetry(RetryContext context) throws Throwable;
}

5.2. RetryTemplate Configuration

5.2.RetryTemplate配置

The RetryTemplate is an implementation of the RetryOperations.

RetryTemplateRetryOperations的一个实现。

Let’s configure a RetryTemplate bean in our @Configuration class:

让我们在@Configuration类中配置一个RetryTemplatebean。

@Configuration
public class AppConfig {
    
    //...
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
		
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(2);
        retryTemplate.setRetryPolicy(retryPolicy);
		
        return retryTemplate;
    }
}

The RetryPolicy determines when an operation should be retried.

RetryPolicy 决定了一个操作何时应该被重试。

A SimpleRetryPolicy is used to retry a fixed number of times. On the other hand, the BackOffPolicy is used to control backoff between retry attempts.

SimpleRetryPolicy用于重试的固定次数。另一方面,BackOffPolicy用于控制重试之间的退避。

Finally, a FixedBackOffPolicy pauses for a fixed period of time before continuing.

最后,一个FixedBackOffPolicy在继续之前会暂停一段固定时间。

5.3. Using the RetryTemplate

5.3.使用RetryTemplate

To run code with retry handling, we can call the retryTemplate.execute() method:

要运行有重试处理的代码,我们可以调用retryTemplate.execute() method

retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
    @Override
    public Void doWithRetry(RetryContext arg0) {
        myService.templateRetryService();
        ...
    }
});

Instead of an anonymous class, we can use a lambda expression:

我们可以使用一个lambda表达式来代替一个匿名类:

retryTemplate.execute(arg0 -> {
    myService.templateRetryService();
    return null;
});

6. Listeners

6.听众

Listeners provide additional callbacks upon retries. And we can use these for various cross-cutting concerns across different retries.

监听器在重试时提供额外的回调。而我们可以利用这些来解决不同重试中的各种交叉问题。

6.1. Adding Callbacks

6.1.添加回调

The callbacks are provided in a RetryListener interface:

回调是在一个RetryListener接口中提供的。

public class DefaultListenerSupport extends RetryListenerSupport {
    
    @Override
    public <T, E extends Throwable> void close(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onClose");
        ...
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onError"); 
        ...
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context,
      RetryCallback<T, E> callback) {
        logger.info("onOpen");
        ...
        return super.open(context, callback);
    }
}

The open and close callbacks come before and after the entire retry, while onError applies to the individual RetryCallback calls.

openclose回调在整个重试之前和之后出现,而onError适用于单个RetryCallback调用。

6.2. Registering the Listener

6.2.注册监听器

Next, we register our listener (DefaultListenerSupport) to our RetryTemplate bean:

接下来,我们将我们的监听器(DefaultListenerSupport)注册到我们的RetryTemplate Bean。

@Configuration
public class AppConfig {
    ...

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        ...
        retryTemplate.registerListener(new DefaultListenerSupport());
        return retryTemplate;
    }
}

7. Testing the Results

7.测试结果

To finish our example, let’s verify the results:

为了完成我们的例子,让我们验证一下结果。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = AppConfig.class,
  loader = AnnotationConfigContextLoader.class)
public class SpringRetryIntegrationTest {

    @Autowired
    private MyService myService;

    @Autowired
    private RetryTemplate retryTemplate;

    @Test(expected = RuntimeException.class)
    public void givenTemplateRetryService_whenCallWithException_thenRetry() {
        retryTemplate.execute(arg0 -> {
            myService.templateRetryService();
            return null;
        });
    }
}

As we can see from the test logs, we have properly configured the RetryTemplate and the RetryListener:

我们可以从测试日志中看到,我们已经正确配置了RetryTemplateRetryListener

2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onOpen 
2020-01-09 20:04:10 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onClose

8. Conclusion

8.结论

In this article, we saw how to use Spring Retry using annotations, the RetryTemplate, and callbacks listeners.

在这篇文章中,我们看到了如何使用注解、RetryTemplate、和回调监听器来使用Spring Retry。

The source code for the examples is available over on GitHub.

这些例子的源代码可以在GitHub上找到