项目中为了保证处理更健壮,容错性更高,更不容易失败,使用自动重试的失败的操作,可提高后续操作的可用性,保证容错性。Spring实提供了自动重试机制,功能简单实用。当错误引起失败是暂时性的情况下,非常适用。比如操作中暂时的网络故障,或者数据库操作由暂时锁引起的异常等。
在微服务中通常都提供了重试与超时配置,比如SpringCloud的Feign组件。在SpringBoot的单应用项目中,我们则可以使用Spring提供的Spring Retry实现自动重试功能。
引入Spring Retry依赖
Spring Retry是从Spring Batch独立出来的一个功能,所以在SpringBoot项目中我们需要在pom.xml文件中引入其依赖:
<!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
启用Spring Retry重试机制
在任一的@Configuration配置类上添加@EnableRetry注解开启Spring Retry的重试功能。
@Configuration
// 开启Retry重试机制
@EnableRetry
public class AppConfig {
}
使用Spring Retry重试功能
项目中我们可以通过两种不同的方式使用Spring Retry重试功能,一种是@Retryable注解的方式,另一种是RetryTemplate方式。
@Retryable注解
通过@RetryAble注解完成自动重试的配置,配置参数详情参考官方文档。这样调用retryAnnotationService方法即完成了自动重试的功能。@Recover注解即多次重试后还是失败则调用该注解的方法,需要位于与@RetryAble注解的方法的同一个类中。
@Service
public class DemoService {
@Retryable(value = Exception.class, maxAttempts = 2,
backoff= @Backoff(delay = 100))
public void retryAnnotationService() throws Exception{
// 调用外部接口,或者连接数据等业务逻辑
System.out.println("service method...");
throw new Exception("Test exception");
}
public Object retryTemplateService() {
// 调用外部接口,或者连接数据等业务逻辑
}
@Recover
public void recover(Exceptione){
System.out.println("recover: " + e.getMessage());
}
}
RetryTemplate
首先配置RetryTemplate并注册为Java Bean交有Spring管理。RetryTemplate的详细配置以及说明可以参考官方文档。
@Configuration
// 开启Retry重试机制
@EnableRetry
public class AppConfig {
@Value("${app.retry.max-attempts:2}")
private Integer maxAttempts;
@Value("${app.retry.back-off-period:1000}")
private Long backOffPeriod;
@Bean
public RetryTemplate retryTemplate() {
/**
* The RetryPolicy determines when an operation should be retried.
* 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.
* Finally, a FixedBackOffPolicy pauses for a fixed period of time before continuing.
*/
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(backOffPeriod);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(maxAttempts);
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.registerListener(new AppRetryListenerSupport());
return retryTemplate;
}
}
实现监听器AppRetryListenerSupport,当重试的时候提供不同触发事件的回调方法,在回调中可以针对不同的触发事件进行处理。
public class AppRetryListenerSupport extends RetryListenerSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(AppRetryListenerSupport.class);
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
LOGGER.info("The retry is closed.");
super.close(context, callback, throwable);
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
LOGGER.info("The retry is on error.");
// 重试的时候如果需要处理一些其他逻辑,可以在该方法内增加
super.onError(context, callback, throwable);
}
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
LOGGER.info("The retry is open.");
return super.open(context, callback);
}
}
使用retryTemplate.execute() 调用需要需要自动重试的方法。
@RestController
@RequestMapping("demo")
public class DemoController {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);
@Autowired
private DemoService demoService;
@Autowired
private RetryTemplate retryTemplate;
@GetMapping
public Object list() {
try {
Object result = retryTemplate.execute(arg -> demoService.retryTemplateService());
return result;
} catch (Exception e) {
LOGGER.error("异常详情: {}", e);
return null;
}
}
}
总结
SpringBoot项目通过使用Spring Retry可以快速的使用自动重试的功能,当项目中调用数据库或者外部接口要保证可用性时,对于操作中暂时性的异常可以通过重试策略保证可用性。本文针对使用层面做了一些总结,未深入研究源码。希望其它网友使用Spring Retry重试功能的时候,能够通过此文获得一些帮助和参考。
参考
https://docs.spring.io/spring-batch/docs/current/reference/html/retry.html