线上服务依赖第三方服务,访问量较大的状况下接口RT响应时间很长。因为每一个超时参数设置为5s。java
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();
复制代码
经过debug,能够知道build方法执行之后,获取到InternalHttpClient对象,若是没有指定执行链,就是用RetryExec执行器,默认的重试策略是DefaultHttpRequestRetryHandlerbash
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);
}
... 省略部分代码
}
复制代码
超时设置的三个维度并发
默认有重试策略,或者能够手动更改或者从新定义重试策略,可是一些状况下,是能够重试,一些状况下重试是失效的。socket
对于咱们的场景应用中的get与post,能够总结为:ide
只有发生IOExecetion时才会发生重试 InterruptedIOException、UnknownHostException、ConnectException、SSLException,发生这4中异常不重试 get方法能够重试3次,post方法在socket对应的输出流没有被write并flush成功时能够重试3次。post
不重试的异常:ui
DefaultHttpRequestRetryHandler 重试策略this
@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种线程中断异常,不会进行重试。spa
post请求在输出流进行write与flush的时候,会发生哪些除了InterruptedIOException、UnknownHostException、ConnectException、SSLException之外的IOExecetion。.net
可能出问题一步在于HttpClientConnection.flush()的一步,跟进去能够得知其操做的对象是一个SocketOutputStream,而这个类的flush是空实现,因此只须要看wirte方法便可。
//根据上下文判断请求是否发送成功了,或者根据状态为是否永远能够重复发送(默认的是否)
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);
}
复制代码