如何为restTemplate实现重试机制

6

我已经实现了一个Java方法,通过Resttemplate调用外部服务。此外,我还在该方法中实现了一些额外的业务逻辑。如何为这些Rest调用实现重试机制。还需要考虑以下几点:

  1. 我不能对整个方法进行重试。
  2. 最好为Rest调用(通过resttemplate)添加重试。
  3. 应该有一种方式可以禁用不需要的Rest调用的重试选项。

我认为你可以这样做。从0到9循环并进行调用。如果成功则终止循环,继续使用结果;否则进行迭代。如果在10次调用中没有返回结果,那么你为什么还要调用该方法浪费时间呢? - Laxminarayan
可能是Retry java RestTemplate HTTP request if host offline的重复问题。 - isank-a
这不是一个好的方法。我已经实现了这些方法。我需要通过最小化当前实现的修改来完成这个任务。如果我们使用通用配置为resttemplate添加重试,那么我可能不需要修改当前的实现。 - Dilshan Niroda
3个回答

12

Spring提供了使用@Retry注解的重试机制。你需要使用以下依赖项。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>

Spring提供以下注解。

Spring重试注解

@EnableRetry - 用于在Spring Boot项目中启用Spring Retry

@Retryable - 表示任何方法都可以作为重试候选方法

@Recover - 指定回退方法

我提供以下示例代码。

@Configuration
@EnableRetry
@SpringBootApplication
public class MyApplication {
}

您可以参考完整示例以了解更多信息。


此注释应添加到方法级别。但对于我的情况,我不能这样做。如果我们从一个 REST 调用中收到错误,则整个方法将再次执行。 - Dilshan Niroda
2
如果你想的话,你可以将方法分成小的内聚函数/方法,然后再添加注释。 - Sambit
此注释有一些限制。特别是,它不能添加到同一类方法和私有方法中。 - Dilshan Niroda
2
是的,有一些限制,但它抽象了很多东西,使得实现变得更容易。 - Sambit
如果你正在使用Spring-boot,你可以直接包含starter spring-boot-starter-batch,以获取与spring-boot版本对齐的依赖项。 - Philippe Simo

11

您可以在 HttpClient 中添加重试机制,并将其用于 RestTemplate,就像这样:

@Bean
public ClientHttpRequestFactory clientFactory() {
    HttpClient httpClient = HttpClients.custom()            
        .setRetryHandler((exception, executionCount, context) -> {
            if (executionCount > 3) {
                log.warn("Maximum retries {} reached", 3);
                return false;
            }
            if (<some condition for retry>) {
                log.warn("Retry {}", executionCount);
                return true;
            }
            return false;
        })
        .build();

    return new HttpComponentsClientHttpRequestFactory(httpClient);
}
@Bean
public RestTemplate customRestTemplate(@Qualifier("clientFactory") ClientHttpRequestFactory clientFactory){ 
    return new RestTemplate(clientFactory);
}

请注意,此解决方案仅适用于低级网络故障。它不会在HTTP状态!= 2xx(例如502)时重试。 - Anne van Leyden
@AnnevanderBom 你找到适用于自定义HTTP状态的解决方案了吗? - Malvon

0

对于 REST API 调用,您可以在客户端级别上实现重试机制,其中实际的 REST 调用正在进行。

 @Retryable(value = Exception.class, maxAttemptsExpression = "${retry.maxAttempts}", backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
    public Optional<T> getApiCall(String url, String token, Class<T> resClass) {
        ResponseEntity<T> response = null;
        try {
            logger.info(url);
            // create headers
            HttpHeaders headers = new HttpHeaders();
            // set `accept` header
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
            headers.setBearerAuth(token);
            // set custom header
            // headers.set("x-request-src", "desktop");
            // build the request
            HttpEntity<String> entity = new HttpEntity<>("", headers);
            // use exchange method for HTTP call
            try {
                response = this.restTemplate.exchange(url, HttpMethod.GET, entity, resClass, 1);
            } catch (HttpStatusCodeException e) {
                return errorService.throwException(HttpStatus.NOT_FOUND, EKDError.LIFPRO406);
            }
            if (response.getStatusCode() == HttpStatus.OK) {
                return Optional.ofNullable(response.getBody());
            } else {
                return errorService.throwException(HttpStatus.NOT_FOUND, EKDError.LIFPRO406);
            }
        } catch (HttpClientErrorException | HttpServerErrorException e) {
            logger.error("Exception in api call : ", e);
            return errorService.throwException(HttpStatus.INTERNAL_SERVER_ERROR, EKDError.LIFPRO407);
        }
    }

现在最大尝试次数和延迟是可配置的,设置应用程序属性maxAttemptsExpression = "${retry.maxAttempts}",backoff = @Backoff(delayExpression = "${retry.maxDelay}"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接