3

【翻译】怎么自定义feign的重试机制

 3 years ago
source link: https://segmentfault.com/a/1190000040677260
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【翻译】怎么自定义feign的重试机制

在微服务框架中,通过rest api的方式调用其他服务是很正常的事情。在spring生态系统中,一个流行的REST客户端是Feign,这是因为它的声名式风格和添加不同配置的DRY方式。

这篇博客中,我会讨论关于feign客户端的重试机制。本能的,我们会这样实现,在try catch和while循环中编写api调用语句,并为另一个api调用编写代码,直到满足条件。这也许能符合我们的目的,但是这会使得我们的代码丑陋且无法实现。

理想情况下,所有东西完美运行,且我们不需要重试任何HTTP请求。因此,在feign中,默认是不启用重试的。然后,完美是不存在的,对于一个tcp包来说,在网络中有数百万种方法会死掉。所以,为了启用重试,你必须把下面的代码放在你的客户端配置中。

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

你可以在default方法中传一些参数,比如:间隔时间、最大重试次数等,否则它会以1秒间隔重试5次。

这仅仅会让feign在碰到IO异常的时候重试。这有点道理,对吧? X 应该重试去获取Y,仅仅当Y不可达的时候。但这并不是经常发生的。有可能,由于Y和Z之间的连接断了,导致Y返回5XX的错误码,并且你想在这种情况下重试。要使用它,你必须抛出RetryableException。为了实现这样的目的,我们需要实现ErrorDecoder类。代码像这样:

public class MyErrorDecoder implements ErrorDecoder {

    private final ErrorDecoder defaultErrorDecoder = new Default();

    @Override
    public Exception decode(String s, Response response) {
        Exception exception = defaultErrorDecoder.decode(s, response);

        if(exception instanceof RetryableException){
            return exception;
        }


        if(response.status() == 504){
            return new RetryableException("504 error", response.request().httpMethod(), null );
        }

        return exception;
    }
}

为了使上述代码生效,你必须把下面的配置放到application properties文件中:

feign.client.config.default.error-decoder=com.example.somepackage.MyErrorDecoder

现在,事情已安排妥当,让我们看看MyErrorDecoder这个类都干了些什么。它实现了ErrorDecoder类并且重写了它的decode方法,这很明显。在decode方法内部,首先我们检查了抛出的异常是不是已经是RetryableException。如果已经是RetryableException,那么这是feign自己抛出的异常,并且如果我们返回该异常,feign就会自己进行重试。

如果异常不是RetryableException,第二段代码会执行。在这段代码中,我们检查返回状态是不是504。如果是,我们手动返回一个RetryableException

我们可以在errorDecoder中干很多事情。想象一个场景,你想在任何5XX的错误码时进行重试,无论这是否是你的实际场景。那么我们应该怎么做?编写一堆if/else嘛?不,你不需要,你只需要:

if (HttpStatus.valueOf(response.status()).is5xxServerError()) {
    return new RetryableException("Server error", response.request().httpMethod(), null);
}

下面,也是自定义重试机制的一个方法。你为啥要这么做?我的场景时,当发生每次重试的时候,我先要打印log。为了定制这个retryer,首先删除配置中的默认retryer。然后创建一个模块,像这样:

@Slf4j
@Component
@NoArgsConstructor
public class CustomRetryer implements Retryer {

    private int retryMaxAttempt;

    private long retryInterval;

    private int attempt = 1;


    public CustomRetryer(int retryMaxAttempt, Long retryInterval) {
        this.retryMaxAttempt = retryMaxAttempt;
        this.retryInterval = retryInterval;
    }

    @Override
    public void continueOrPropagate(RetryableException e) {
        log.info("Feign retry attempt {} due to {} ", attempt, e.getMessage());

        if(attempt++ == retryMaxAttempt){
            throw e;
        }
        try {
            Thread.sleep(retryInterval);
        } catch (InterruptedException ignored) {
            Thread.currentThread().interrupt();
        }

    }

    @Override
    public Retryer clone() {
        return new CustomRetryer(6, 2000L);
    }
}

这里我们的CustomRetryer重写了continueOrPropagateclone方法,这是feign默认retryer的方法。clone方法中,我们以需要的参数创建了一个CustomRetryer,这里6是最大重试次数,2000L时每次重试的间隔时间。

continueOrPropagate方法中,你可以定制你的重试机制。记住,为了停止重试并且传播错误信息,你必须抛出这个方法收到的retryable异常。否则,它会继续重试。在这个例子中,我们在尝试我们设定的最大重试次数之后,抛出这个异常,否则它会在继续下一次重试之前,等待间隔时间(参数)。

到目前为止,我们看到的是如何创建一个自定义的错误解码器和重传器,以根据我们的需要扩展feign的可靠性。如果您以这种方式创建错误解码器和重试器,它将为您添加到项目中的任意数量的feign客户端工作。但是,想象一个场景,对于不同的client,你想要不通的重试机制,或者对屿其他的的client,不进行重试。你要怎么做?给不通的client,绑定不通的重试器和编码器是很容易的。像这样配置就行:

feign.client.config.default.<your_client_name>.error-decoder=com.example.somepackage.MyErrorDecoderfeign.client.config.client1.retryer=com.example.somepackage.CustomRetryer

重试快乐!!

原文地址:https://medium.com/swlh/how-t...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK