Java retry on error

Руководство по Spring Retry

Spring Retry предоставляет возможность автоматического повторного вызова неудачной операции. Это полезно, когда ошибки могут быть временными (например, кратковременный сбой сети). Spring Retry обеспечивает декларативное управление процессом и поведением на основе политик, которое легко расширять и настраивать.

В этой статье мы увидим, как использоватьSpring Retry для реализации логики повтора в приложениях Spring. Мы также настроим слушателей для получения дополнительных обратных вызовов.

2. Maven Зависимости

Начнем с добавления зависимости в нашpom.xml:

 org.springframework.retry spring-retry 1.1.5.RELEASE 

Мы можем проверить последнюю версиюspring-retry вMaven Central.

Нам также нужно добавить Spring AOP в наш pom.xml:

 org.springframework spring-aspects 

3. Включение Spring Retry

Чтобы включить Spring Retry в приложении, нам нужно добавить аннотацию@EnableRetry в наш класс@Configuration:

@Configuration @EnableRetry public class AppConfig

4. Повторить с аннотациями

Мы можем сделать вызов метода, который будет повторен в случае неудачи, используя аннотации.

4.1. @Retryableс

Чтобы добавить в методы функцию повтора, можно использовать@Retryable:

@Service public interface MyService < @Retryable( value = < SQLException.class >, maxAttempts = 2, backoff = @Backoff(delay = 5000)) void retryService(String sql) throws SQLException; . >

Здесь поведение повтора настраивается с использованием атрибутов@Retryable.. В этом примере попытка повтора будет предпринята, только если метод выдаетSQLException.. Будет до 2 повторов и задержка в 5000 миллисекунд.

Если@Retryable используется без каких-либо атрибутов, если метод не работает из-за исключения, то повторная попытка будет предпринята до трех раз с задержкой в ​​одну секунду.

4.2. @Recoverс

Аннотация@Recover используется для определения отдельного метода восстановления, когда метод@Retryable дает сбой с указанным исключением:

@Service public interface MyService

Поэтому, если методretryService() выдаетSQLException, будет вызван методrecover(). Подходящий обработчик восстановления имеет свой первый параметр типаThrowable (необязательно). S ubsequent аргументы заполняются из списка аргументов метода с ошибкой в ​​том же порядке, что и метод с ошибкой, и с тем же типом возвращаемого значения.

Читайте также:  How to code with python

5. RetryTemplateс

5.1 RetryOperations

Spring Retry предоставляет интерфейсRetryOperations, который предоставляет набор методовexecute():

public interface RetryOperations < T execute(RetryCallback retryCallback) throws Exception; . >

RetryCallback, который является параметромexecute(), представляет собой интерфейс, который позволяет вставлять бизнес-логику, которую необходимо повторить в случае сбоя:

public interface RetryCallback

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() < @Override public Void doWithRetry(RetryContext arg0) < myService.templateRetryService(); . >>);

То же самое может быть достигнуто с помощью лямбда-выражения вместо анонимного класса: __

6. Конфигурация XML

Spring Retry может быть настроен с помощью XML с использованием пространства имен Spring AOP.

6.1. Добавление файла XML

В пути к классам добавимretryadvice.xml:

В этом примере используется пользовательскийRetryTemplate внутри перехватчика методаxmlRetryService.

6.2. Использование конфигурации XML

Импортируйтеretryadvice.xml изclasspath и включите поддержку@AspectJ:

@Configuration @EnableRetry @EnableAspectJAutoProxy @ImportResource("classpath:/retryadvice.xml") public class AppConfig

7. Слушатели

Слушатели предоставляют дополнительные обратные вызовы при повторных попытках. Их можно использовать для решения различных сквозных задач при разных попытках.

7.1. Добавление обратных вызовов

Обратные вызовы предоставляются в интерфейсеRetryListener:

public class DefaultListenerSupport extends RetryListenerSupport < @Override public void close(RetryContext context, RetryCallback callback, Throwable throwable) < logger.info("onClose); . super.close(context, callback, throwable); >@Override public void onError(RetryContext context, RetryCallback callback, Throwable throwable) < logger.info("onError"); . super.onError(context, callback, throwable); >@Override public boolean open(RetryContext context, RetryCallback callback) < logger.info("onOpen); . return super.open(context, callback); >>

Обратные вызовыopen иclose поступают до и после всего повтора, аonError применяется к отдельным вызовамRetryCallback.

7.2. Регистрация слушателя

Затем мы регистрируем наш слушатель (DefaultListenerSupport) в нашем bean-компонентеRetryTemplate:

@Configuration public class AppConfig < . @Bean public RetryTemplate retryTemplate() < RetryTemplate retryTemplate = new RetryTemplate(); . retryTemplate.registerListener(new DefaultListenerSupport()); return retryTemplate; >>

8. Тестирование результатов

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes = AppConfig.class, loader = AnnotationConfigContextLoader.class) public class SpringRetryTest < @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 и Listener:

2017-01-09 20:04:10 [main] INFO o.b.s.DefaultListenerSupport - onOpen 2017-01-09 20:04:10 [main] INFO o.example.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2017-01-09 20:04:10 [main] INFO o.b.s.DefaultListenerSupport - onError 2017-01-09 20:04:12 [main] INFO o.example.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService() 2017-01-09 20:04:12 [main] INFO o.b.s.DefaultListenerSupport - onError 2017-01-09 20:04:12 [main] INFO o.b.s.DefaultListenerSupport - onClose

9. Заключение

В этой статье мы представили Spring Retry. Мы видели примеры повторных попыток с использованием аннотаций иRetryTemplate. Затем мы настроили дополнительные обратные вызовы с использованием слушателей.

Читайте также:  Тип int python границы

Вы можете найти исходный код этой статьиover on GitHub.

Источник

spring webclient: retry with backoff on specific error

Update: With new API the same solution will be:

 .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(10)) .filter(this::is5xxServerError)); //. private boolean is5xxServerError(Throwable throwable)

You can do this taking the following approach:

  • Use the exchange() method to obtain the response without an exception, and then throw a specific (custom) exception on a 5xx response (this differs from retrieve() which will always throw WebClientResponseException with either a 4xx or 5xx status);
  • Intercept this specific exception in your retry logic;
  • Use reactor-extra — it contains a nice way to use retryWhen() for more complex & specific retries. You can then specify a random backoff retry that starts after 10 seconds, goes up to an arbitrary time and tries a maximum of 3 times. (Or you can use the other available methods to pick a different strategy of course.)
//. webclient .exchange() .flatMap(clientResponse -> < if (clientResponse.statusCode().is5xxServerError()) < return Mono.error(new ServerErrorException()); >else < //Any further processing >>).retryWhen( Retry.anyOf(ServerErrorException.class) .randomBackoff(Duration.ofSeconds(10), Duration.ofHours(1)) .maxRetries(3) ) ); 

Inside the retryWhen() can we use different Retry policies for different Exception ? Let’s say for some exception we’d like a higer maxRetries for example ?

retryWhen(reactor.retry.Retry) method is deprecated and to be removed in v3.4. But use retryWhen(reactor.util.retry.Retry)

the retryWhen with Retry.anyOf and Retry.onlyIf are deprecated I assume. I found this approach useful, and it allows us to process and throw a User defined exception.

retryWhen(Retry.backoff(3, Duration.of(2, ChronoUnit.SECONDS)) .filter(error -> error instanceof UserDefinedException/AnyOtherException) .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> new UserDefinedException(retrySignal.failure().getMessage()))) 
// . .retryWhen( backoff(maxAttempts, minBackoff) .filter(throwable -> ((WebClientResponseException) throwable).getStatusCode().is5xxServerError())) // . 

Adding only withThrowable to your existing code can make it work. This has worked for me. You can try something like this :

.retryWhen(withThrowable(Retry.any() .doOnRetry(e -> log .debug("Retrying to data for <> due to exception: <>", employeeId, e.exception().getMessage())) .retryMax(config.getServices().getRetryAttempts()) .backoff(Backoff.fixed(Duration.ofSeconds(config.getServices().getRetryBackoffSeconds()))))) 
 .retryWhen(retryBackoffSpec()) private RetryBackoffSpec retryBackoffSpec() < return Retry.backoff(RETRY_ATTEMPTS, Duration.ofSeconds(RETRY_DELAY)) .filter(throwable ->throwable instanceof yourException); > 

The class reactor.retry.Retry from reactor-extra is deprecated and should be avoided. Use the reactor.util.Retry class from reactor-core .

Читайте также:  Php filter all post data

There is a convenient way to map response statuses to concrete exceptions using the onStatus method.

So, if you want to retry on 5xx status codes, a simple solution would be to throw a CustomException on 5xx and retry only when exception is CustomException .

// Retry only on 5xx webClient.post() .retrieve() .onStatus(HttpStatusCode::is5xxClientError, clientResponse -> Mono.error(new CustomException())) .bodyToMono(. ) .retryWhen(Retry.max(3).filter(t -> t instanceof CustomException)) // Alternatively if you don't want to fail/retry on certain status code you can also return an empty `Mono` to ignore the error and propagate the response webClient.post() .retrieve() .onStatus(httpStatusCode -> httpStatusCode.value() == 404, clientResponse -> Mono.empty()) .bodyToMono(. ) .retryWhen(Retry.max(3).filter(t -> t instanceof CustomException)) 

Источник

Functionality for automatic retry after exception

My code still, applies — use the anonymous class. Your method is somewhat unpleasant with the null and the assignment and the break . Also the generics in my approach are much cleaner than your Object .

My advice is to split the task from its execution; I would pass a Runnable or a Custom interface to AutoRetry; this decouples what you are doing from the retry policy

1 Answer 1

This looks pretty good, but I would split the running task from the retry. Also use generics, don’t just throw Object about.

Use a Java 8 lambda and the return of the method:

public static Optional doWithRetry(final Supplier t) < for (int retry = 0; retry catch (InterruptedException | UnknownHostException e) < LOGGER.log(Level.SEVERE, "Call failed.", e); return Optional.empty(); >catch (IOException e) < LOGGER.log(Level.WARNING, "Call failed. Retry.", e); >> LOGGER.log(Level.SEVERE, "Call failed. Retries exceeded."); return Optional.empty(); > 

Also, use a real logger, not printStackTrace .

final String data = doWithRetry(() -> < //do stuff >); 

If your lambda needs to throw an exception, you’ll need to define your own @FunctionalInterface :

@FunctionalInterface interface StuffDoer

And use that in the method signature, you’ll need to handle generic Exception .

final String data = doWithRetry(new StuffDoer() < @Override public T get() throws Exception < return null; >>); 

Источник

Оцените статью