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.

Итак, вопросы:

  1. Верна ли приведенная выше реализация retryTemplate? Если нет, как его реализовать и вызвать? Другой вариант, который я пытался реализовать (но не продвинулся далеко), заключался в использовании RetryListenerSupport в клиентском методе и вызове retryTemplate внутри метода onError.

  2. Должны ли мы увеличить количество повторов на 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 попытки.

Другие вопросы по тегам