#java #web-services #apache-httpclient-4.x #tls1.2 #tcp-keepalive
#java #веб-сервисы #apache-httpclient-4.x #tls1.2 #tcp-keepalive
Вопрос:
Я использую Apache HttpClient 4.5 для своего веб-сервиса soap.
В настоящее время я столкнулся с проблемой, из-за которой функция keep alive в httpclient игнорируется при использовании TLSv1.2. Однако функция поддержания работоспособности работает при использовании HTTP.
У вас, ребята, есть какая-то идея по этому поводу?
Мой код показан как показано ниже:
Основной класс: 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;
}
}
Класс триггера: 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. Использует ли ваше приложение аутентификацию клиента на основе сертификата?
2. Привет, ok2c, я думаю, да. Я сделаю следующее: 1. поместите хранилище ключей и truststore в KeyManager и TrustManager 2. поместите KeyManager и TrustManager в SSLContext 3. создайте org.apache.http.conn.ssl.SSLConnectionSocketFactory, указав SSLContext, а протокол — TLSv1.2 4. создайте PoolingHttpClientConnectionManager, указав SSLConnectionSocketFactory
3. Это не решит вашу непосредственную проблему, но, по крайней мере, объясняет, почему HttpClient не использует повторно постоянные соединения, которые не используют один и тот же контекст сеанса / выполнения
4. Привет, ok2c, спасибо за отзыв. Я не могу использовать httpclient в том же контексте выполнения, потому что он вызывается из другого класса. Класс реализации A -> webServiceClient -> HttpClientPool. Или у вас есть образец HttpClientPool для использования в том же контексте выполнения?
Ответ №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()); } } }
-
Если вы уверены, что ваше приложение не должно поддерживать несколько удостоверений пользователя, отключите отслеживание состояния соединения.
httpClient = HttpClients.custom() .disableConnectionState() .build();
Комментарии:
1. Привет, ok2c, я попробовал оба метода и хорошо работаю. Я буду использовать 1-й вариант, поскольку он может обрабатывать несколько удостоверений пользователя. Я действительно ценю ваш отзыв. Это действительно мне очень помогает.