在使用Spring Boot中的resilience4j-retry达到最大尝试次数后处理异常。

huangapple go评论75阅读模式
英文:

Handle exception after reaching max attempts in resilience4j-retry using Spring Boot

问题

我有一个场景,我想记录每次重试尝试以及当最后一次尝试失败(即达到maxAttempts次数)时抛出异常,假设还会创建一个数据库条目。

我尝试使用Resilience4j-retry与Spring Boot来实现这一点,因此我使用application.yml和注解。

@Retry(name = "default", fallbackMethod="fallback")
@CircuitBreaker(name = "default", fallbackMethod="fallback")
public ResponseEntity<List<Person>> person() {
    return restTemplate.exchange(...);               // 假设这总是抛出500
}

回退方法将异常的原因记录到应用程序日志中。

public ResponseEntity<?> fallback(Exception e) {
    var status = HttpStatus.INTERNAL_SERVER_ERROR;
    var cause = "未知原因";
    if (e instanceof ResourceAccessException) {
        var resourceAccessException = (ResourceAccessException) e;
        if (e.getCause() instanceof ConnectTimeoutException) {
            cause = "连接超时";
        }
        if (e.getCause() instanceof SocketTimeoutException) {
            cause = "读取超时";
        }
    } else if (e instanceof HttpServerErrorException) {
        var httpServerErrorException = (HttpServerErrorException) e;
        cause = "服务器错误";
    } else if (e instanceof HttpClientErrorException) {
        var httpClientErrorException = (HttpClientErrorException) e;
        cause = "客户端错误";
    } else if (e instanceof CallNotPermittedException) {
        var callNotPermittedException = (CallNotPermittedException) e;
        cause = "断路器打开";
    }
    var message = String.format("%s 导致回退,捕获异常 %s", 
        cause, e.getMessage());
    log.error(message);                                         // 应用程序日志条目
    throw new MyRestException (message, e);
}

当我调用这个person()方法时,重试会发生,直到达到配置的maxAttempt次数。我期望我的自定义运行时异常MyRestException在每次重试时被捕获,并在最后一次重试(达到maxAttempt次数)时被抛出,因此我在try-catch中包装了调用。

public List<Person> person() {
    try {
        return myRestService.person().getBody();
    } catch (MyRestException ex) {
        log.error("在这里,我准备将问题记录到数据库中");
        throw new ex;
    }
}

不幸的是,重试似乎被忽略了,因为回退遇到并重新抛出异常,该异常立即在我的try-catch中捕获,而不是使用Resilience4j-retry机制。

如何在达到maxAttempts时实现所需的行为?是否有办法为这种情况定义特定的回退方法?

英文:

I have a scenario I want to log each retry attempt and when the last one fails (i.e. maxAttempts reached) a exception is thrown and let's say an entry to a database is created.

I try to achieve this using Resilience4j-retry with Spring Boot, therefore I use application.yml and annotations.

@Retry(name = &quot;default&quot;, fallbackMethod=&quot;fallback&quot;)
@CircuitBreaker(name = &quot;default&quot;, fallbackMethod=&quot;fallback&quot;)
public ResponseEntity&lt;List&lt;Person&gt;&gt; person() {
    return restTemplate.exchange(...);               // let&#39;s say this always throws 500
}

The fallback logs the cause of the exception into an application log.

public ResponseEntity&lt;?&gt; fallback(Exception e) {
    var status = HttpStatus.INTERNAL_SERVER_ERROR;
    var cause = &quot;Something unknown&quot;;
    if (e instanceof ResourceAccessException) {
        var resourceAccessException = (ResourceAccessException) e;
        if (e.getCause() instanceof ConnectTimeoutException) {
            cause = &quot;Connection timeout&quot;;
        }
        if (e.getCause() instanceof SocketTimeoutException) {
            cause = &quot;Read timeout&quot;;
        }
    } else if (e instanceof HttpServerErrorException) {
        var httpServerErrorException = (HttpServerErrorException) e;
        cause = &quot;Server error&quot;;
    } else if (e instanceof HttpClientErrorException) {
        var httpClientErrorException = (HttpClientErrorException) e;
        cause = &quot;Client error&quot;;
     } else if (e instanceof CallNotPermittedException) {
        var callNotPermittedException = (CallNotPermittedException) e;
        cause = &quot;Open circuit breaker&quot;;
    }
    var message = String.format(&quot;%s caused fallback, caught exception %s&quot;, 
        cause, e.getMessage());
    log.error(message);                                         // application log entry
    throw new MyRestException (message, e);
}

When I call this method person() the retry happens as maxAttempt configured. I expect my custom runtime MyRestException is caught on each retry and thrown on the last one (when maxAttempt is reached), so I wrap the call in the try-catch.

public List&lt;Person&gt; person() {
    try {
        return myRestService.person().getBody();
    } catch (MyRestException ex) {
        log.error(&quot;Here I am ready to log the issue into the database&quot;);
        throw new ex;
    }
}

Unfortunatelly, the retry seems to be ignored as the fallback encounters and rethrows the exception that is immediatelly caught with my try-catch instead of the Resilience4j-retry mechanism.

How to achieve the behavior when the maxAttempts is hit? Is there a way to define a specific fallback method for such case?

答案1

得分: 1

为什么不在您的服务方法内部捕获并将异常映射到MyRestException?例如,myRestService.person()?这会使您的配置更加简单,因为您只需要将MyRestException 添加到RetryConfig和CircuitBreakerConfig的配置中。

Spring RestTemplate还具有机制,可以注册自定义的ResponseErrorHandler,如果您不想将样板代码添加到每个服务方法中。-> https://www.baeldung.com/spring-rest-template-error-handling

我不会将CallNotPermittedException映射到MyRestException。在断路器打开时,您不希望重试。将CallNotPermittedException添加到RetryConfig中的忽略异常列表中。

我认为您根本不需要回退机制。我认为将异常映射到另一个异常不是一种“回退”。

英文:

Why don't you catch and map exceptions to MyRestException inside of your Service methods, e.g. myRestService.person()?
It makes your configuration even simpler, because you only have to add MyRestException to the configuration of your RetryConfig and CircuitBreakerConfig.

Spring RestTemplate also has mechanisms to register a custom ResponseErrorHandler, if you don't want to add the boilerplate code to every Service method. -> https://www.baeldung.com/spring-rest-template-error-handling

I would not map CallNotPermittedException to MyRestException. You don't want to retry when the CircuitBreaker is open. Add CallNotPermittedException to the list of ignored exceptions in your RetryConfig.

I think you don't need the fallback mechanism at all. I thing mapping an exception to another exception is not a "fallback".

huangapple
  • 本文由 发表于 2020年10月13日 20:46:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/64335573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定