【问题标题】:Spring Retry with RetryTemplate in Spring Boot, Java8在 Spring Boot,Java8 中使用 RetryTemplate 进行 Spring 重试
【发布时间】:2020-06-22 00:14:14
【问题描述】:

我正在使用 Spring Boot 2.1.14.RELEASE、Java8、Spring Boot。 我有一个客户,我必须从中访问另一个休息服务。 我需要重试 Http404 和 HTTP500 2 次,而不重试任何其他异常。

我正在使用 RestTemplate 来调用 rest 服务,如下所示:

restTemplate.postForEntity(restUrl, requestEntity, String.class);

我研究了使用 Retryable 和 RetryTemplate 并使用 RetryTemplate 实现了重试功能。

我通过两种方式实现了这一点:

选项1:

RetryTemplate bean 是:

@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 次重试。所以在调用recovery回调方法之前,总共有3个请求提交到rest服务。

我的观察是:客户端正在提交 2 个休息服务请求。

根据我阅读的有关 RetryTemplate 的内容,上述观察是有意义的。

所以问题是:

  1. 上述retryTemplate的实现是否正确?如果没有,如何实现和调用它?我尝试实现的另一个选项(但没有取得任何进展)是在客户端方法上使用 RetryListenerSupport 并在 onError 方法中调用 retryTemplate。

  2. 我们是否应该将重试次数增加 1 以达到预期效果?我已经尝试过了,它得到了我需要的东西,但创建 RetryTemplate 时并没有考虑到这个目的。

OPTION2:上面#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 对象所必需的。如何实现?

【问题讨论】:

    标签: spring spring-boot java-8 spring-retry retrytemplate


    【解决方案1】:

    simpleRetryPolicy.setMaxAttempts(2);

    表示总共 2 次尝试,而不是 2 次重试。

    【讨论】:

    • 同意。那么实现所需目标的正确方法是什么?您认为 Option2 是正确的实现方式吗?如果是,那么有没有办法将对象从客户端类传递给侦听器类?
    • 我个人会使用选项 1;在侦听器中重新调用模板似乎是错误的。
    • 谢谢。但是使用 Option1,我需要将 retryAttempts 设置为 1+实现所需的初始请求和重试过程所需的实际重试次数。这是可接受的用法吗?
    • 它叫maxAttempts not retryAttempts, not maxRetries;请参阅SimpleRetryPolicy javadocs /** * 设置重试次数用尽之前的尝试次数。包括重试开始之前的初始 * 尝试,因此通常为 {@code >= 1}。例如 * 将此属性设置为 3 意味着总共尝试 3 次(初始 + 2 次重试)。 * @param maxAttempts 最大尝试次数,包括初始尝试。 */
    猜你喜欢
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 2017-10-21
    • 2017-04-22
    • 2019-04-01
    • 2018-02-01
    相关资源
    最近更新 更多