httpclient超时重试记录

httpclient超时重试记录线上服务依赖第三方服务,访问量较大的情况下接口RT响应时间很长。由于每个超时参数设置为5s。 get方法可以重试3次,post方法在socket对应的输出流没有被write并flush成功时可以重试3次。 这两种超时都是SocketTimeoutException,继承自Int…

背景

线上服务依赖第三方服务,访问量较大的情况下接口RT响应时间很长。由于每个超时参数设置为5s。

实例

PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
// 连接池最大的连接数
connManager.setMaxTotal(6000);
// 单个路由的最大连接数,例如:www.baidu.com,图片最大高峰并发量520
connManager.setDefaultMaxPerRoute(500);

RequestConfig.Builder custom = RequestConfig.custom();
// 连接超时时间 建立连接时间 此前设置3000
custom.setConnectTimeout(800);
// 从connect Manager获取Connection 超时时间(连接池获取连接超时时间)
custom.setConnectionRequestTimeout(500);
// (获取response的返回时间)读取返回时间 此前设置40000,官方建议40s
custom.setSocketTimeout(1000);
RequestConfig config = custom.build();

// DefaultHttpRequestRetryHandler 重试策略(可自定义)
httpClient = HttpClients.custom().setRetryHandler(new DefaultHttpRequestRetryHandler(1, true)).setConnectionManager(connManager)
                .setDefaultRequestConfig(config).build();

构造httpClient

通过debug,可以知道build方法执行以后,获取到InternalHttpClient对象,如果没有指定执行链,就是用RetryExec执行器,默认的重试策略是DefaultHttpRequestRetryHandler

public CloseableHttpClient 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);
    }
    ... 省略部分代码
}

超时参数

超时设置的三个维度

  • 连接超时 setConnectTimeout
  • 读取超时 setSocketTimeout
  • 从连接池获取连接超时 setConnectionRequestTimeout

重试策略对业务的影响

默认有重试策略,或者可以手动更改或者重新定义重试策略,但是一些情况下,是可以重试,一些情况下重试是失效的。

重试分析

对于我们的场景应用中的get与post,可以总结为:

只有发生IOExecetion时才会发生重试 InterruptedIOException、UnknownHostException、ConnectException、SSLException,发生这4中异常不重试 get方法可以重试3次,post方法在socket对应的输出流没有被write并flush成功时可以重试3次。

不重试的异常:

  • InterruptedIOException,线程中断异常
  • UnknownHostException,找不到对应host
  • ConnectException,找到了host,连接建立失败
  • SSLException,https认证异常

DefaultHttpRequestRetryHandler 重试策略

@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;
    } else {
        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;
    }

    // 根据上下文判断请求是否发送成功了,或者根据状态为是否永远可以重复发送(默认的是否) requestSentRetryEnabled 参数可以在构建httpCLient对象设置重试策略指定此属性
    // isRequestSent 是在HttpRequestExecutor中doSendRequest方法中flush成功设置context.setAttribute("http.request_sent", Boolean.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;
}

// 几种类型的异常不重试,同时判断是否与异常类型的子类,是子类的话,也不重试

另外,两种超时,连接超时与读超时: java.net.SocketTimeoutException: Read timed out java.net.SocketTimeoutException: connect timed out 这两种超时都是SocketTimeoutException,继承自InterruptedIOException,属于上面的第1种线程中断异常,不会进行重试。

哪些情况下会进行重试

post请求在输出流进行write与flush的时候,会发生哪些除了InterruptedIOException、UnknownHostException、ConnectException、SSLException以外的IOExecetion。

可能出问题一步在于HttpClientConnection.flush()的一步,跟进去可以得知其操作的对象是一个SocketOutputStream,而这个类的flush是空实现,所以只需要看wirte方法即可。

重试requestSentRetryEnabled http.request_sent参数

//根据上下文判断请求是否发送成功了,或者根据状态为是否永远可以重复发送(默认的是否)
if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {
    return true;
}

禁止重试

httpclient默认提供了重试策略,对于一些场景下,我们可以手动关闭重试策略。HttpClientBuilder中,其build()方法中之所以选择了RetryExec执行器是有前置条件的,即没有手动禁止。

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

参考

blog.csdn.net/gaohe7091/a…

今天的文章httpclient超时重试记录分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15756.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注