拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Feign如何重试失败的调用REST Api

Feign如何重试失败的调用REST Api

白鹭 - 2022-03-07 2309 0 2

一、简介

通过REST 端点调用外部服务是一种常见的活动,像Feign 这样的库非常简单。但是,在此类通话期间,很多事情都可能出错。其中会出现许多是随机的或暂时的问题。

在本教程中,我们将学习如何重试失败的调用并创建更具弹性的REST 客户端。

2. Feign 客户端设置

首先,让我们创建一个简单的Feign 客户端构建器,稍后我们将通过重试功能对其进行增强。我们将使用OkHttpClient作为HTTP 客户端。此外,我们将使用GsonEncoderGsonDecoder对请求和响应进行编码和解码。最后,我们需要指定目标的URI 和响应类型:

public class ResilientFeignClientBuilder {
 public static <T> T createClient(Class<T> type, String uri) {
 return Feign.builder()
 .client(new OkHttpClient())
 .encoder(new GsonEncoder())
 .decoder(new GsonDecoder())
 .target(type, uri);
 }
 }

或者,如果我们使用Spring,我们可以让它使用可用的bean 自动连接Feign 客户端。

3. Feign Retryer

幸运的是,重试能力是在Feign 中烘焙的,只需要配置它们。我们可以通过向客户端构建器Retryer接口的实现来做到这一点。

它最重要的方法continueOrPropagate,接受RetryableException作为参数并且不返回任何内容。执行时,它要么抛出异常,要么成功退出(通常在休眠后)。如果没有抛出异常,Feign 会继续重试调用。如果抛出异常,它将被传播并有效地完成调用并出现错误。

3.1 简单实现

让我们编写一个非常简单的Retryer 实现,它总是会在等待一秒钟后重试调用:

public class NaiveRetryer implements feign.Retryer {
 @Override
 public void continueOrPropagate(RetryableException e) {
 try {
 Thread.sleep(1000L);
 } catch (InterruptedException ex) {
 Thread.currentThread().interrupt();
 throw e;
 }
 }
 }

因为Retryer实现了Cloneable接口,所以我们还需要重写clone方法。

@Override
 public Retryer clone() {
 return new NaiveRetryer();
 }

最后,我们需要将我们的实现添加到客户端构建器中:

public static <T> T createClient(Class<T> type, String uri) {
 return Feign.builder()
 // ...
 .retryer(new NaiveRetryer())
 // ...
 }

或者,如果我们使用Spring,我们可以使用@Component NaiveRetryer,或者在配置类中定义一个bean,让Spring 完成剩下的工作:

@Bean
 public Retryer retryer() {
 return new NaiveRetryer();
 }

3.2.默认实现

Retryer接口的合理默认实现。它只会重试给定的次数,从某个时间间隔开始,然后随着每次重试将其增加到提供的最大值。让我们定义它的起始间隔为100 毫秒,最大间隔为3 秒,最大尝试次数为5:

public static <T> T createClient(Class<T> type, String uri) {
 return Feign.builder()
 // ...
 .retryer(new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5))
 // ...
 }

3.3.不重试

如果我们不希望Feign 重试任何调用,我们可以为客户端构建器Retryer.NEVER_RETRY它每次都会简单地传播异常。

4. 创建可重试异常

在上一节中,我们学会了控制重试调用的频率。现在让我们看看如何控制何时重试调用以及何时简单地抛出异常。

4.1ErrorDecoderRetryableException

当我们收到错误的响应时,Feign 将其传递给ErrorDecoder接口的一个实例,该接口决定如何处理它。最重要的是,解码器可以将异常映射到RetryableException,的实例,使Retryer能够重试调用。**ErrorDecoder的默认实现仅在响应包含“Retry-After”标头时RetryableExeception**最常见的是,我们可以在503 Service Unavailable 响应中找到它。

这是很好的默认行为,但有时我们需要更加灵活。例如,我们可能正在与一个外部服务通信,该服务不时随机响应500 Internal Server Error,而我们无权修复它。我们能做的就是重试调用,因为我们知道它下次可能会起作用。为此,我们需要编写一个自定义的ErrorDecoder实现。

4.2.创建自定义错误解码器

我们只需要在自定义解码器中实现一种方法:decode它接受两个参数,一个String方法键和一个Response对象。它返回一个异常,它应该是RetryableException的实例还是其他依赖于实现的异常。

我们的decode方法将简单地检查响应的状态码是否高于或等于500。如果是这种情况,它将创建RetryableException如果没有,它将返回FeignExceptionerrorStatus工厂函数创建的FeignException

public class Custom5xxErrorDecoder implements ErrorDecoder {
 @Override
 public Exception decode(String methodKey, Response response) {
 FeignException exception = feign.FeignException.errorStatus(methodKey, response);
 int status = response.status();
 if (status >= 500) {
 return new RetryableException(
 response.status(),
 exception.getMessage(),
 response.request().httpMethod(),
 exception,
 null,
 response.request());
 }
 return exception;
 }
 }

请注意,在这种情况下,我们创建并返回异常,而不是抛出它。

最后,我们需要在客户端构建器中插入我们的解码器:

public static <T> T createClient(Class<T> type, String uri) {
 return Feign.builder()
 // ...
 .errorDecoder(new Custom5xxErrorDecoder())
 // ...
 }

五、总结

在本文中,我们学习了如何控制Feign 库的重试逻辑。我们研究了Retryer界面以及如何使用它来操纵时间和重试尝试次数。然后我们创建了我们的ErrorDecoder来控制哪些响应需要重试。


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *