Apache HttpClient - 使用 SSL 或 TLSv1.2 时忽略 keepalive

Apache HttpClient - keepalive is ignored when using SSL or TLSv1.2

我正在为我的 soap 网络服务使用 Apache HttpClient 4.5。

目前我遇到了使用TLSv1.2时httpclient中的keep alive被忽略的问题。但是,如果使用 HTTP,keep alive 可以正常工作。

你们有什么想法吗?

我的代码如下:

主要Class:HttpClientPool.java

public class HttpClientPool {

    private static PoolingHttpClientConnectionManager manager = null;
    private static CloseableHttpClient httpClient = null;
    private static final Logger logger = Logger.getLogger(HttpClientPool.class);

    public static synchronized CloseableHttpClient getHttpClient(){

        if(httpClient==null){

            //Some function to get SSLConnectionSocketFactory in Singleton
            SSLConnectionSocketFactory sslConnSocFac = getSSLConnectionSocketFactory();

            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("https", sslConnSocFac)
                    .build();

            HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory = new ManagedHttpClientConnectionFactory(
                    DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE);

            DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

            manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connectionFactory, dnsResolver);

            SocketConfig deaultSocketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
            manager.setDefaultSocketConfig(deaultSocketConfig);
            manager.setMaxTotal(300);
            manager.setDefaultMaxPerRoute(200);
            manager.setValidateAfterInactivity(50*1000);

            RequestConfig defaultRequestConfig = RequestConfig.custom()
                    .setConnectTimeout(20*1000)
                    .setSocketTimeout(50*1000)
                    .setConnectionRequestTimeout(20000)
                    .build();

            ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
                public long getKeepAliveDuration(HttpResponse httpResponse, org.apache.http.protocol.HttpContext context) {
                    return 1000 * 1000;
                }
            };

            httpClient = HttpClients.custom()
                    .setConnectionManager(manager)
                    .setConnectionManagerShared(false)
                    .evictIdleConnections(60l, TimeUnit.SECONDS)
                    .evictExpiredConnections()
                    .setConnectionTimeToLive(60, TimeUnit.SECONDS)
                    .setDefaultRequestConfig(defaultRequestConfig)
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                    .setKeepAliveStrategy(myStrategy)
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                    .build();

            Runtime.getRuntime().addShutdownHook(new Thread(){
                @Override
                public void run(){
                    try {
                        httpClient1.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        return httpClient;
    }

    private static SSLConnectionSocketFactory getSSLConnectionSocketFactory() {
        //some working
        return sslConnectionSocketFactory;
    }

}

触发器Class:webServiceClient.java

public class webServiceClient{

    HttpClientPool httpClientPool;

    private static final Logger logger = Logger.getLogger(HttpClientPool.class);

    public sendSOAPMessage(String url, String soapAction){
        HttpPost post = new HttpPost(url);
        HttpEntity entity = new ByteArrayEntity(xml.getBytes("UTF-8"));

        post.setEntity(entity);
        post.setHeader("Content-type", "application/soap+xml; charset=UTF-8");
        post.setHeader("SOAPAction", soapAction);
        post.setHeader("Connection", "Keep-Alive");
        post.setHeader("Keep-Alive", "header");

        CloseableHttpResponse response = httpClientPool.getHttpClient().execute(post);
        String result = EntityUtils.toString(response.getEntity());
        logger.info("Response: " + result);

        EntityUtils.consume(response.getEntity());
        response.close();
    }
}

这里有两个选择:

  1. 将用户令牌(在您的情况下应该是用户证书的 CN)作为参数传递给 #sendSOAPMessage

    public class webServiceClient{
    
        HttpClientPool httpClientPool;
    
        private static final Logger logger = Logger.getLogger(HttpClientPool.class);
    
        public sendSOAPMessage(String url, String soapAction, String userToken){
            HttpPost post = new HttpPost(url);
            HttpEntity entity = new ByteArrayEntity(xml.getBytes("UTF-8"));
    
            post.setEntity(entity);
            post.setHeader("Content-type", "application/soap+xml; charset=UTF-8");
            post.setHeader("SOAPAction", soapAction);
            post.setHeader("Connection", "Keep-Alive");
            post.setHeader("Keep-Alive", "header");
    
            HttpClientContext clientContext = HttpClientContext.create();
            clientContext.setUserToken(userToken);
            try (CloseableHttpResponse response = httpClientPool.getHttpClient().execute(post, clientContext)) {
                String result = EntityUtils.toString(response.getEntity());
                logger.info("Response: " + result);
    
                EntityUtils.consume(response.getEntity());
            }
        }
    }
    
  2. 如果您确定您的应用程序不必支持多个用户身份,请禁用连接状态跟踪。

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