美文网首页
HttpClient的两种重试机制

HttpClient的两种重试机制

作者: Java旺 | 来源:发表于2020-11-06 16:21 被阅读0次

    本文基于 HttpClient 4.5.13

    使用 http 请求外部服务时,由于网络或者服务本身的不稳定性,经常需要重试。重试当然可以通过手撸代码实现,但更好的方式是通过现有的机制去实现。 HttpClient 中支持两种重试:

    异常重试。
    服务不可用重试。
    异常重试
    HttpClient 执行时会抛出两种异常:

    java.io.IOException
    ClientProtocolException
    

    java.io.IOException 被认为是非致命性且可恢复的,而 ClientProtocolException 被认为是致命性的,不可恢复。

    处理的时候要注意, ClientProtocolException 是 java.io.IOException 的子类。

    如果是这样创建 HttpClient 的

    CloseableHttpClient httpClient = HttpClients.custom().build();
    

    异常重试是默认开启的,具体代码可以参考 HttpClientBuilder.build() 方法,下面是相关的代码

    // Add request retry executor, if not disabled
    if (!automaticRetriesDisabled) {
        HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
        if (retryHandlerCopy == null) {
            retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
        }
        execChain = new RetryExec(execChain, retryHandlerCopy);
    }
    

    automaticRetriesDisabled 是一个 boolean 类型的变量,默认为 false ,所以条件默认是成立的,如果没有设置 HttpRequestRetryHandler 就会用一个默认的。

    DefaultHttpRequestRetryHandler 主要有三个成员变量

    retryCount
    requestSentRetryEnabled
    nonRetriableClasses
    

    默认的实例变量设置如下

    retryCount=3 ,最多重试3次。
    requestSentRetryEnabled=false ,发送成功的就不会重试了
    nonRetriableClasses 包含了以下四种:InterruptedIOException UnknownHostException ConnectException SSLException
    重试的执行逻辑在 org.apache.http.impl.execchain.RetryExec ,有兴趣的可以去看下。

    默认的是否重试逻辑如下

    @Override
    public boolean retryRequest(final IOException exception, final int executionCount, final HttpContext context) {
        Args.notNull(exception, "Exception parameter");
        Args.notNull(context, "HTTP context");
        if (executionCount > this.retryCount) {
            // Do not retry if over max retry count
            // 超过重试次数不重试
            return false;
        }
        // 如果是忽略的异常不重试
        if (this.nonRetriableClasses.contains(exception.getClass())) {
            return false;
        }
        for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {
            if (rejectException.isInstance(exception)) {
                return false;
            }
        }
        final HttpClientContext clientContext = HttpClientContext.adapt(context);
        final HttpRequest request = clientContext.getRequest();
    
        if(requestIsAborted(request)){
            return false;
        }
        // 幂等方法可以重试
        if (handleAsIdempotent(request)) {
            // Retry if the request is considered idempotent
            return true;
        }
        // 如果请求没有发送或者发送了也重试
        if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {
            // Retry if the request has not been sent fully or
            // if it's OK to retry methods that have been sent
            return true;
        }
        // otherwise do not retry
        return false;
    }
    

    这里要注意下幂等方法, post 和 put 都不是,所以那里的判断不会成立,但是如果 requestSentRetryEnabled 设置为 true ,还是会重发的,那就需要保证被调用的接口再处理 post 和 put 的请求时是幂等的。

    有些人可能会遇到问题,比如报了 SocketTimeoutException 的异常,但是没有重试,这是因为 SocketTimeoutException 是 InterruptedIOException 的子类,默认会被忽略。如果需要重试,可以自定义一个 HttpRequestRetryHandler ,然后再设置就可以了。

    HttpClients.custom().setRetryHandler(httpRequestRetryHandler).build();
    实际上使用的时候继承 DefaultHttpRequestRetryHandler ,然后扩展一些自己的实现就很方便。

    如果想禁用调重试也很简单

    HttpClients.custom().disableAutomaticRetries().build();
    

    服务不可用重试
    有的时候,请求成功了,但是 http 状态码可能不是 2xx,这种情况也需要重试。 HttpClient中提供了在服务不可用时进行重试的机制。

    重试执行的逻辑在 org.apache.http.impl.execchain.ServiceUnavailableRetryExec ,有兴趣可以看下。

    HttpClient 中提供了默认的策略,但是没有默认开启,需要自己设置

    DefaultServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new DefaultServiceUnavailableRetryStrategy();
    httpClient = HttpClients.custom().setServiceUnavailableRetryStrategy(serviceUnavailableRetryStrategy).build();
    

    重试的逻辑

    @Override
    public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
        return executionCount <= maxRetries &&
            response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
    }
    

    当没有超过重试次数,且返回码为503的时候进行重试,当然也可以自定义 ServiceUnavailableRetryStrategy 来实现自己的需求。

    另外还支持设置重试请求的间隔时间。

    来源:https://www.tuicool.com/articles/R73AzmF

    相关文章

      网友评论

          本文标题:HttpClient的两种重试机制

          本文链接:https://www.haomeiwen.com/subject/ddwtbktx.html