Spring Retry с RetryTemplate в Spring Boot, Java8
Я использую Spring Boot 2.1.14.RELEASE, Java8, Spring Boot. У меня есть клиент, с которого мне нужно получить доступ к другому сервису отдыха. Мне нужно повторить попытку Http404 и HTTP500 2 раза, не повторяя никаких других исключений.
Я использую RestTemplate для вызова остальной службы следующим образом:
restTemplate.postForEntity(restUrl, requestEntity, String.class);
Я изучил использование Retryable, а также RetryTemplate и реализовал функцию повтора с помощью RetryTemplate.
Я реализовал это двумя способами:
ОПЦИЯ 1:
Bean-компонент RetryTemplate:
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(retryProperties.getDelayForCall());
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
retryTemplate.setRetryPolicy(exceptionClassifierRetryPolicy);
return retryTemplate;
}
ClassifierRetryPolicy - это:
@Component
public class ExceptionClassifierRetryPolicy1 extends ExceptionClassifierRetryPolicy {
@Inject
private RetryProperties retryProperties;
public ExceptionClassifierRetryPolicy1(){
final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts(2);
this.setExceptionClassifier(new Classifier<Throwable, RetryPolicy>() {
@Override
public RetryPolicy classify(Throwable classifiable) {
if (classifiable instanceof HttpServerErrorException) {
// For specifically 500
if (((HttpServerErrorException) classifiable).getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
return simpleRetryPolicy;
}
return new NeverRetryPolicy();
}
else if (classifiable instanceof HttpClientErrorException) {
// For specifically 404
if (((HttpClientErrorException) classifiable).getStatusCode() == HttpStatus.NOT_FOUND) {
return simpleRetryPolicy;
}
return new NeverRetryPolicy();
}
return new NeverRetryPolicy();
}
});
}
}
В моем клиентском классе я использую retryTemplate следующим образом:
public void postToRestService(...,...){
...
retryTemplate.execute(context -> {
logger.info("Processing request...");
responseEntity[0] = restTemplate.postForEntity(restURL, requestEntity, String.class);
return null;
}, context -> recoveryCallback(context));
...
}
Остальная вызываемая служба бросает HTTP404 при каждом запросе.
Мое ожидание: клиент должен отправить один запрос, получить HTTP404 и выполнить 2 попытки. Таким образом, до вызова метода обратного вызова восстановления в службу отдыха отправлено 3 запроса.
Мое наблюдение: клиент отправляет 2 запроса в службу отдыха.
Приведенное выше наблюдение имеет смысл из того, что я читал о RetryTemplate.
Итак, вопросы:
Верна ли приведенная выше реализация retryTemplate? Если нет, как его реализовать и вызвать? Другой вариант, который я пытался реализовать (но не продвинулся далеко), заключался в использовании RetryListenerSupport в клиентском методе и вызове retryTemplate внутри метода onError.
Должны ли мы увеличить количество повторов на 1, чтобы достичь желаемого? Я пробовал это, и это дает мне то, что мне нужно, но RetryTemplate создан не для этой цели.
ВАРИАНТ 2: вариант реализации кода, упомянутый в пункте 1 выше:
Клиентский метод:
@Retryable(listeners = "RestClientListener")
public void postToRestService(...,...){
...
responseEntity[0] = restTemplate.postForEntity(restURL, requestEntity, String.class);
...
}
Слушатель:
public class RestClientListener extends RetryListenerSupport {
private static final Logger logger = LoggerFactory.getLogger(RestClientListener.class);
@Inject
RestTemplate restTemplate;
@Inject
RetryTemplate retryTemplate;
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
logger.info("Retrying count for RestClientListener "+context.getRetryCount());
...
final ResponseEntity<String>[] responseEntity = new ResponseEntity[]{null};
if( context.getLastThrowable().getCause() != null &&
(context.getLastThrowable().getCause() instanceof RestClientResponseException &&
((RestClientResponseException) context.getLastThrowable().getCause()).getRawStatusCode() == HttpStatus.NOT_FOUND.value()))
{
logger.info("Retrying now: ", context.getLastThrowable().toString());
retryTemplate.execute(context2 -> {
logger.info("Processing request...: {}", context2);
responseEntity[0] = restTemplate.postForEntity(restURL, requestEntity, String.class);
return responseEntity;
}, context2 -> recoveryCallback(context2));
}
else {
// Only retry for the above if condition
context.setExhaustedOnly();
}
}
}
Проблема с этим подходом заключается в том, что я не могу найти способ обмена объектами между моим клиентом и классами clientListener. Эти объекты необходимы для создания объектов requestEntity и header. Как этого добиться?
1 ответ
simpleRetryPolicy.setMaxAttempts(2);
Означает всего 2 попытки, а не 2 попытки.