问题
多线程进行http调用接口,使用PoolingHttpClientConnectionManager,连接池。线程一直Running,无法关闭。
配置及版本
httpclient:4.5.12
httpclient的RequestConfig 配置
/**
* 配置信息
* @param isProxy 是否代理
* @return RequestConfig requestConfig
*/
public RequestConfig getRequestConfig(Boolean isProxy,Integer sockTimeout) {
if(isProxy) {
return RequestConfig.custom()
.setSocketTimeout(sockTimeout)
.setConnectTimeout(5000)
.setProxy(new HttpHost(proxyHost, proxyPort ))
.setConnectionRequestTimeout(5000)
.build();
}else {
return RequestConfig.custom()
.setSocketTimeout(sockTimeout)
.setConnectTimeout(1000)
.setConnectionRequestTimeout(1000)
.build();
}
}
PoolingHttpClientConnectionManager配置信息
private static final PoolingHttpClientConnectionManager HTTP_CLIENT_POOL = new PoolingHttpClientConnectionManager();
static {
HTTP_CLIENT_POOL.setMaxTotal(5000);
HTTP_CLIENT_POOL.setDefaultMaxPerRoute(500);
}
分析问题
打印线程信息
对应代码
private boolean createTunnelToTarget(
final AuthState proxyAuthState,
final HttpClientConnection managedConn,
final HttpRoute route,
final HttpRequest request,
final HttpClientContext context) throws HttpException, IOException {
final RequestConfig config = context.getRequestConfig();
final int timeout = config.getConnectTimeout();
final HttpHost target = route.getTargetHost();
final HttpHost proxy = route.getProxyHost();
HttpResponse response = null;
final String authority = target.toHostString();
final HttpRequest connect = new BasicHttpRequest("CONNECT", authority, request.getProtocolVersion());
this.requestExecutor.preProcess(connect, this.proxyHttpProcessor, context);
while (response == null) {
if (!managedConn.isOpen()) {
this.connManager.connect(
managedConn,
route,
timeout > 0 ? timeout : 0,
context);
}
connect.removeHeaders(AUTH.PROXY_AUTH_RESP);
this.authenticator.generateAuthResponse(connect, proxyAuthState, context);
response = this.requestExecutor.execute(connect, managedConn, context);
this.requestExecutor.postProcess(response, this.proxyHttpProcessor, context);
final int status = response.getStatusLine().getStatusCode();
if (status < 200) {
throw new HttpException("Unexpected response to CONNECT request: " +
response.getStatusLine());
}
if (config.isAuthenticationEnabled()) {
if (this.authenticator.isAuthenticationRequested(proxy, response,
this.proxyAuthStrategy, proxyAuthState, context)) {
if (this.authenticator.handleAuthChallenge(proxy, response,
this.proxyAuthStrategy, proxyAuthState, context)) {
// Retry request
if (this.reuseStrategy.keepAlive(response, context)) {
this.log.debug("Connection kept alive");
// Consume response content
final HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} else {
managedConn.close();
}
response = null;
}
}
}
}
final int status = response.getStatusLine().getStatusCode();
if (status > 299) {
// Buffer response content
final HttpEntity entity = response.getEntity();
if (entity != null) {
response.setEntity(new BufferedHttpEntity(entity));
}
managedConn.close();
throw new TunnelRefusedException("CONNECT refused by proxy: " +
response.getStatusLine(), response);
}
// How to decide on security of the tunnelled connection?
// The socket factory knows only about the segment to the proxy.
// Even if that is secure, the hop to the target may be insecure.
// Leave it to derived classes, consider insecure by default here.
return false;
}
注意while死循环。
对比配置,发现未配置PoolingHttpClientConnectionManager的超时
增加PoolingHttpClientConnectionManager
解决问题
PoolingHttpClientConnectionManager connPool = new PoolingHttpClientConnectionManager();
// 对连接池设置SocketConfig对象
connPool.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(10000).build());