【问题标题】:Trying to exclude an exception using @Retryable - causes ExhaustedRetryException to be thrown尝试使用 @Retryable 排除异常 - 导致 ExhaustedRetryException 被抛出
【发布时间】:2016-11-30 19:36:38
【问题描述】:

我正在尝试在调用 REST 模板的方法上使用 @Retryable。如果由于通信错误而返回错误,我想重试,否则我只想在调用时抛出异常。

当 ApiException 发生时,而不是被 @Retryable 抛出和忽略,我得到一个 ExhaustedRetryException 和一个关于没有找到足够的“可恢复”的抱怨,即 @Recover 方法。

我想我会看看是否存在可恢复的方法是否可以让它快乐并仍然按预期执行。没那么多。它没有抛出异常,而是调用了可恢复方法。

@Retryable(exclude = ApiException include = ConnectionException, maxAttempts = 5, backoff = @Backoff(multiplier = 2.5d, maxDelay = 1000000L, delay = 150000L))
Object call(String domainUri, ParameterizedTypeReference type, Optional<?> domain = Optional.empty(), HttpMethod httpMethod = HttpMethod.POST) throws RestClientException {

    RequestEntity request = apiRequestFactory.createRequest(domainUri, domain, httpMethod)
    log.info "************************** Request Entity **************************"
    log.info "${request.toString()}"
    ResponseEntity response

    try {

        response = restTemplate.exchange(request, type)
        log.info "************************** Response Entity **************************"
        log.info "${response.toString()}"

    } catch (HttpStatusCodeException | HttpMessageNotWritableException httpException) {

        String errorMessage
        String exceptionClass = httpException.class.name.concat("-")
        if(httpException instanceof HttpStatusCodeException) {

            log.info "************************** API Error **************************"
            log.error("API responded with errors: ${httpException.responseBodyAsString}")
            ApiError apiError = buildErrorResponse(httpException.responseBodyAsString)
            errorMessage = extractErrorMessage(apiError)

            if(isHttpCommunicationError(httpException.getStatusCode().value())) {
                throw new ConnectionException(exceptionClass.concat(errorMessage))
            }
        }

        errorMessage = StringUtils.isBlank(errorMessage) ? exceptionClass.concat(httpException.message) : exceptionClass.concat(errorMessage)
        throw new ApiException(httpMethod, domainUri, errorMessage)

    }

    if (type.type == ResponseEntity) {
        response
    }
    else response.body

}

@Recover
Object connectionException(ConnectionException connEx) {
    log.error("Retry failure - communicaiton error")
    throw new ConnectionException(connEx.class.name + " - " + connEx.message)
}

任何见解将不胜感激。是错误还是操作员错误? 这是使用 Spring Boot 1.3.6 和 Spring-Retry 1.1.3。

【问题讨论】:

    标签: java spring groovy spring-retry


    【解决方案1】:

    您的包含/排除语法看起来很糟糕 - 甚至无法编译。

    我刚刚写了一个快速测试,如果你有零个@Recover 方法,它的工作方式完全符合预期......

    package com.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.retry.annotation.EnableRetry;
    import org.springframework.retry.annotation.Retryable;
    
    @SpringBootApplication
    @EnableRetry
    public class So38601998Application {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(So38601998Application.class, args);
            Foo bean = context.getBean(Foo.class);
            try {
                bean.out("foo");
            }
            catch (Exception e) {
                System.out.println(e);
            }
            try {
                bean.out("bar");
            }
            catch (Exception e) {
                System.out.println(e);
            }
        }
    
    
        @Bean
        public Foo foo() {
            return new Foo();
        }
    
        public static class Foo {
    
            @Retryable(include = IllegalArgumentException.class, exclude = IllegalStateException.class,
                    maxAttempts = 5)
            public void out(String foo) {
                System.out.println(foo);
                if (foo.equals("foo")) {
                    throw new IllegalArgumentException();
                }
                else {
                    throw new IllegalStateException();
                }
            }
    
        }
    
    }
    

    结果:

    foo
    foo
    foo
    foo
    foo
    java.lang.IllegalArgumentException
    bar
    java.lang.IllegalStateException
    

    如果你只是添加

    @Recover
    public void connectionException(IllegalArgumentException e) {
        System.out.println("Retry failure");
    }
    

    你得到

    foo
    foo
    foo
    foo
    foo
    Retry failure
    bar
    org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is java.lang.IllegalStateException
    

    所以你需要一个包罗万象的@Recover 方法...

    @Recover
    public void connectionException(Exception e) throws Exception {
        System.out.println("Retry failure");
        throw e;
    }
    

    结果:

    foo
    foo
    foo
    foo
    foo
    Retry failure
    bar
    Retry failure
    java.lang.IllegalStateException
    

    【讨论】:

    • 你能澄清一下语法有什么问题吗?我没有收到任何编译错误。我会尝试添加一个毯子@Recover 方法。
    • 所以我输入了一个包罗万象的@Recover 方法,它得到的异常就是我说要排除的异常。
    • 正确 - 如果您至少有一个 @Recover 方法,即使对于排除(未重试)的异常,您也需要一个包罗万象的方法;它可以像我的一样重新抛出)。重新语法:比较你的:exclude = ApiException include = ConnectionException, 和我的:include = IllegalArgumentException.class, exclude = IllegalStateException.class, - 需要逗号和.class
    • 这是在某个地方记录的吗?也许我看到了它并掩盖了它。我的代码是 Groovy - Groovy 不需要 .class(也不需要分号!)。
    • 我不知道 groovy;你在其他地方都有逗号,所以我觉得很奇怪。恢复与重试正交——RetryTemplate 获得一个策略,RecoveryCallback——该策略决定是否重试特定异常;为所有失败调用恢复回调(如果提供) - 即使是那些未重试的失败 - 请参阅 RetryTemplate 的 Javadocs ... Keep executing the callback until it either succeeds or the policy dictates that we stop, in which case the recovery callback will be executed. 如果您没有 @Recover,则没有 RecoveryCallback。跨度>
    猜你喜欢
    • 1970-01-01
    • 2015-07-09
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 2011-11-23
    • 2015-07-16
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多