1、概述
Spring Retry 是 Spring 框架中的一个组件,
它提供了主动从新调用失败操作的能力。这在谬误可能是临时产生的(如刹时网络故障)的状况下很有帮忙。
在本文中,咱们将看到应用 Spring Retry 的各种形式:注解、RetryTemplate 以及回调。
<!–more–>
2、Maven 依赖
让咱们首先将 spring-retry
依赖项增加到咱们的 pom.xml
文件中:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
咱们还须要将 Spring AOP 增加到咱们的我的项目中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
能够查看 Maven Central 来获取最新版本的 spring-retry
和spring-aspects
依赖项。
3、开启 Spring Retry
要在应用程序中启用 Spring Retry,咱们须要将 @EnableRetry
正文增加到咱们的 @Configuration
类:
@Configuration
@EnableRetry
public class AppConfig {...}
4、应用 Spring Retry
4.1、@Retryable
而不必复原
咱们能够应用 @Retryable
注解为办法增加重试性能:
@Service
public interface MyService {@Retryable(value = RuntimeException.class)
void retryService(String sql);
}
在这里,当抛出 RuntimeException 时尝试重试。
依据 @Retryable 的默认行为,重试最多可能产生 3 次,重试之间有 1 秒的提早。
4.2、@Retryable
和@Recover
当初让咱们应用 @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()
办法将被调用。
复原处理程序的第一个参数应该是 Throwable
类型(可选)和雷同的返回类型。其余的参数按雷同程序从失败办法的参数列表中填充。
4.3、自定义 @Retryable
的行为
为了自定义重试的行为,咱们能够应用参数 maxAttempts
和backoff
:
@Service
public interface MyService {
@Retryable( value = SQLException.class,
maxAttempts = 2, backoff = @Backoff(delay = 100))
void retryServiceWithCustomization(String sql) throws SQLException;
}
这样最多将有两次尝试和 100 毫秒的提早。
4.4、应用 Spring Properties
咱们还能够在 @Retryable
注解中应用 properties。
为了演示这一点,咱们将看到如何将 delay
和maxAttempts
的值内部化到一个 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;
}
请留神,咱们当初应用的是 maxAttemptsExpression
和delayExpression
而不是 maxAttempts
和delay
。
5、RetryTemplate
5.1、RetryOperations
Spring Retry 提供了 RetryOperations
接口,它提供了一组 execute()
办法:
public interface RetryOperations {<T> T execute(RetryCallback<T> retryCallback) throws Exception;
...
}
execute()
办法的参数RetryCallback
,是一个接口,能够插入须要在失败时重试的业务逻辑:
public interface RetryCallback<T> {T doWithRetry(RetryContext context) throws Throwable;
}
5.2、RetryTemplate
配置
RetryTemplate
是 RetryOperations
的一个实现。
让咱们在 @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
用于管制重试尝试之间的回退。
最初,FixedBackOffPolicy
会使重试在持续之前暂停一段固定的工夫。
5.3、应用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;
});
6、监听器
监听器在重试时提供另外的回调。咱们能够用这些来关注跨不同重试的各个横切点。
6.1、增加回调
回调在 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);
}
}
open
和 close
的回调在整个重试之前和之后执行,而 onError
利用于单个 RetryCallback
调用。
6.2、注册监听器
接下来,咱们将咱们的监听器 (DefaultListenerSupport)
注册到咱们的RetryTemplate
bean:
@Configuration
public class AppConfig {
...
@Bean
public RetryTemplate retryTemplate() {RetryTemplate retryTemplate = new RetryTemplate();
...
retryTemplate.registerListener(new DefaultListenerSupport());
return retryTemplate;
}
}
7、测试后果
为了实现咱们的示例,让咱们验证一下后果:
@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;
});
}
}
从测试日志中能够看出,咱们曾经正确配置了 RetryTemplate
和RetryListener
:
2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onOpen
2020-01-09 20:04:10 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onError
2020-01-09 20:04:12 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onError
2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onClose
8、论断
在本文中,咱们看到了如何应用注解、RetryTemplate
和回调监听器来应用 Spring Retry。
原文地址:https://www.baeldung.com/spri…
翻译:码农熊猫
更多技术干货,请拜访我的集体网站 https://pinmost.com,或关注公众号【码农熊猫】