Spring Retry使用

介绍

Spring Retry提供了自动重新调用失败操作的能力,这对于暂时性的错误情况(例如一时的网络故障)很有帮助。

在本教程中,我们将看到Spring Retry的各种使用方法:annotations, RetryTemplate and callbacks.

添加maven依赖

在pom.xml中添加如下依赖

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

  

启用Spring Retry

在项目中启用Spring Retry,我们需要添加@EnableRetry注解到@Configuration类

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

使用Spring Retry

@Retryable Without Recovery

在需要重试的方法上加上@Retryable注解

@Service
public interface MyService {
    @Retryable(value = RuntimeException.class)
    void retryService(String sql);

}

  在这里,当抛出RuntimeException异常时尝试重新执行方法,重试最多可以发生三次,重试之间的延迟为一秒。

@Retryable and @Recover

@Service
public interface MyService {
    @Retryable(value = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException;
        
    @Recover
    void recover(SQLException e, String sql);
} 

 

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

因此,如果retryServiceWithRecovery()方法在三次尝试后持续抛出SqlException,将调用recover()方法。

定制@Retryable行为

为了定制重试的行为,我们可以使用参数maxAttempts和backoff:

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

最多会有两次尝试,延迟100毫秒。

使用Spring Properties

定义retryConfig.properties属性文件

retry.maxAttempts=2
retry.maxDelay=100

然后我们指示@Configuration类加载这个文件:

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

 

最后,在@Retryable注解中注入retry.maxAttempts 和 retry.maxDelay

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

 RetryTemplate

在@Configuration类中配置一个RetryTemplate bean:

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

  RetryPolicy决定何时重试操作,SimpleRetryPolicy用于定义尝试重试次数,另一方面,BackOffPolicy用于控制重试尝试之间的间隔时间。

 Using the RetryTemplate

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

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

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

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

  

Listeners监听器

监听器在重试时提供额外的回调。我们可以使用这些在不同的重试中用于各种横切关注点。

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

  注册监听器

@Configuration
public class AppConfig {
    ...

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

  测试结果

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

  

12:56:42.577 [main] INFO com.baeldung.springretry.DefaultListenerSupport - onOpen
12:56:42.577 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=0
12:56:42.581 [main] INFO com.baeldung.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
12:56:42.581 [main] INFO com.baeldung.springretry.DefaultListenerSupport - onError
12:56:44.582 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=1
12:56:44.582 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry: count=1
12:56:44.582 [main] INFO com.baeldung.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
12:56:44.582 [main] INFO com.baeldung.springretry.DefaultListenerSupport - onError
12:56:44.582 [main] DEBUG org.springframework.retry.support.RetryTemplate - Checking for rethrow: count=2
12:56:44.582 [main] DEBUG org.springframework.retry.support.RetryTemplate - Retry failed last attempt: count=2
12:56:44.582 [main] INFO com.baeldung.springretry.DefaultListenerSupport - onClose

结论

在这篇文章中,我们看到了如何使用注释来使用Spring Retry,the RetryTemplate 以及 callbacks listeners.

posted on 2021-12-16 17:54  rabbit-xf  阅读(253)  评论(0)    收藏  举报