前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HttpComponents HttpClient连接池(7)-重试

HttpComponents HttpClient连接池(7)-重试

作者头像
TA码字
发布2020-04-07 16:16:36
1.7K0
发布2020-04-07 16:16:36
举报
文章被收录于专栏:TA码字TA码字

上一篇文章里我们介绍了 httpclient 连接池中空闲连接的清理,在这里我们主要介绍 http 连接的重试机制。

http连接的重试

httpclient 连接池也支持请求的重试,即在请求失败的情况下进行重试,对于重试设计以下几个关键点。

  1. 如何开启重试
  2. 如何定义重试次数
  3. 如何进行重试

如何开启重试

在 httpclient 连接池中,连接发送请求的重试是由 HttpRequestRetryHandler 类型的对象来处理,在HttpClientBuilder 有方法 disableAutomaticRetries() 来关闭重试,默认情况下重试是开启的,所以如果希望禁止重试那么就调用此方法。然后对于HttpClientBuilder 在构建 httpclient 的时候会根据这设置来确定 HttpRequestRetryHandler ,核心代码如下:

代码语言:javascript
复制
if (!automaticRetriesDisabled) {
    HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
if (retryHandlerCopy == null) {
        retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
    }
    execChain = new RetryExec(execChain, retryHandlerCopy);
}
  • httpclient 默认是开启重试机制的,如果希望关闭重试,则在构造中调用 HttpClientBuilder 的 disableAutomaticRetries() 方法。
  • 构造过程中如果开启了重试,那么则设置对象实例 HttpRequestRetryHandler 来负责重试。
  • HttpRequestRetryHandler 可以通过 Builder 对象来在外部设置。
  • 如果没有设置 HttpRequestRetryHandler 实例, 那么默认的为 DefaultHttpRequestRetryHandler 类型。

如何定义重试次数

如果使用默认重试机制,那么重试次数定义在 DefaultHttpRequestRetryHandler 对象实例中,其核心代码如下:

代码语言:javascript
复制
public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler();

public DefaultHttpRequestRetryHandler() {
    this(3, false);
}

public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
    this(retryCount, requestSentRetryEnabled, Arrays.asList(InterruptedIOException.class, UnknownHostException.class, ConnectException.class,SSLException.class));
}

protected DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled, final Collection<Class<? extends IOException>> clazzes) {
    super();
    this.retryCount = retryCount;
    this.requestSentRetryEnabled = requestSentRetryEnabled;
    this.nonRetriableClasses = new HashSet<Class<? extends IOException>>();
    for (final Class<? extends IOException> clazz: clazzes) {
        this.nonRetriableClasses.add(clazz);
    }
}
  • 在 DefaultHttpRequestRetryHandler 对象的默认构造函数中重试次数为 3。
  • 同时定义发生什么类型的 Exception 不会进行重试,在默认构造函数中所定义的这些异常类型为InterruptedIOException/UnknownHostException/ConnectException/SSLExcetion 。

如何进行重试

重试的过程定义在 RetryExec 对象实例的 execute() 方法里,其核心代码为:

代码语言:javascript
复制
for (int execCount = 1;; execCount++) {
    try {
        return this.requestExecutor.execute(route, request, context, execAware);
     } catch (final IOException ex) {
            if (execAware != null && execAware.isAborted()) {
                this.log.debug("Request has been aborted");
                throw ex;
            }
            if (retryHandler.retryRequest(ex, execCount, context)) {
                if (this.log.isInfoEnabled()) {
                    this.log.info("I/O exception ("+ ex.getClass().getName() + ") caught when processing request to "+ route +": "+ ex.getMessage());
                }
                if (this.log.isDebugEnabled()) {
                    this.log.debug(ex.getMessage(), ex);
                }
                if (!RequestEntityProxy.isRepeatable(request)) {
                    this.log.debug("Cannot retry non-repeatable request");
                    throw new NonRepeatableRequestException("Cannot retry request " + "with a non-repeatable request entity", ex);
                }
                request.setHeaders(origheaders);
                if (this.log.isInfoEnabled()) {
                    this.log.info("Retrying request to " + route);
                }
          } else {
                if (ex instanceof NoHttpResponseException) {
                    final NoHttpResponseException updatedex = new NoHttpResponseException(route.getTargetHost().toHostString() + " failed to respond");
                    updatedex.setStackTrace(ex.getStackTrace());
                    throw updatedex;
                }
                throw ex;
            }
        }
}
  • 根据设置的重试次数进行循环发 http 请求,如果出现 IOException 则进行重试尝试。
  • 如果请求 abort ,则取消重试。
  • 对于请求 abort 的定义, 是指调了以前文章介绍的 ManagedHttpClientConnection 类型对象的shutdown 方法,绕过 TCP4 次握手关闭 socket 连接,直接设置 linger 通过发送 RST(reset) 包来快速关闭连接,核心代码如下:
代码语言:javascript
复制
public void shutdown() throws IOException {
    final Socket socket = this.socketHolder.getAndSet(null);
    if (socket != null) {
        // force abortive close (RST)
        try {
            socket.setSoLinger(true, 0);
        } catch (final IOException ex) {
        // empty here  
        } 
        finally {
            socket.close();
        }
    }
}
  • 如果 retryHandler 决定重试,那么就进行下一次重试,然后重试次数减 1。
  • 对于 DefaultHttpRequestRetryHandler 对象实例决定是否重试逻辑如下:
代码语言:javascript
复制
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;
}
  • 如何到达重试次数则不重试。
  • 如果请求 abort 则不重试。
  • 如果发生的异常在 retryHanlder 不进行重试的异常名单里或者是名单里的实例,则不重试。在默认构造函数之中所定义的这些异常类型为InterruptedIOException/UnknownHostException/ConnectException/SSLExcetion 。
  • 如果是 idempotent 幂等类型请求则重试, get 为幂等类型请求。
  • 如果是请求没有发出去或者 retryHandler 的 requestSentRetryEnabled 属性为 true 则重试,该值默认为 false 。
  • 除以上情况外的其他情况均不重试。

目前先写到这里,在下一篇文章里我们开始介绍 httpclient 连接池对于 ssl 支持。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TA码字 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档