1. Обзор
Spring Retry предоставляет возможность автоматического повторного вызова неудачной операции. Это полезно, когда ошибки могут быть временными (например, мгновенный сбой сети).
В этом руководстве мы увидим различные способы использования Spring Retry : аннотации, RetryTemplate
и обратные вызовы.
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
по умолчанию повторная попытка может выполняться до трех раз с задержкой в одну секунду между повторными попытками.
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
.
Аннотация @Recover
определяет отдельный метод восстановления, когда метод @Retryable
дает сбой с указанным исключением.
Следовательно, если метод retryServiceWithRecovery продолжает вызывать исключение
SqlException
после трех попыток, будет вызван метод recovery ()
.
Обработчик восстановления должен иметь первый параметр типа 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. Использование свойств пружины
Мы также можем использовать свойства в аннотации @Retryable
.
Чтобы продемонстрировать это, мы увидим, как внедрить значения delay
и maxAttempts
в файл свойств.
Во-первых, давайте определим свойства в файле с именем retryConfig.
свойства
:
retry.maxAttempts=2
retry.maxDelay=100
Затем мы приказываем нашему классу @Configuration
загрузить этот файл:
// ...
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }
Наконец, мы можем ввести значения retry.maxAttempts
и retry.maxDelay
в наше определение @Retryable
:
@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. Повторить шаблон
5.1. RetryOperations
Spring Retry предоставляет интерфейс RetryOperations
, который предоставляет набор методов execute()
:
public interface RetryOperations {
<T> T execute(RetryCallback<T> retryCallback) throws Exception;
...
}
RetryCallback , который является параметром execute()
, представляет собой интерфейс, позволяющий вставлять бизнес-логику, которую необходимо повторить в случае сбоя :
``
public interface RetryCallback<T> {
T doWithRetry(RetryContext context) throws Throwable;
}
5.2. Конфигурация RetryTemplate
RetryTemplate — это
реализация RetryOperations
.
Давайте настроим bean-компонент RetryTemplate
в нашем классе @Configuration
:
@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
Чтобы запустить код с обработкой повторных попыток, мы можем вызвать метод r etryTemplate.execute
()
: ``
retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
@Override
public Void doWithRetry(RetryContext arg0) {
myService.templateRetryService();
...
}
});
Вместо анонимного класса мы можем использовать лямбда-выражение: ``
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)
в нашем bean-компоненте RetryTemplate
:
@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 o.b.s.DefaultListenerSupport - onOpen
2020-01-09 20:04:10 [main] INFO o.foreach.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.foreach.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. Заключение
В этой статье мы увидели, как использовать Spring Retry, используя аннотации, RetryTemplate
и прослушиватели обратных вызовов.
Исходный код примеров доступен на GitHub .