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.
浙公网安备 33010602011771号