在实现PoolighttpClientConnectionManager时遇到问题
我试图在java代码中实现http连接池,当我尝试使用它时,我得到一个握手异常。如果我去掉设置连接管理器的那一行,它就会工作。这对我来说毫无意义。我正在使用这些jar文件: httpclient-4.5.2.jar httpcore-4.4.4.jar 连接池就绪后:在实现PoolighttpClientConnectionManager时遇到问题,http,ssl,connection-pooling,ssl-client-authentication,Http,Ssl,Connection Pooling,Ssl Client Authentication,我试图在java代码中实现http连接池,当我尝试使用它时,我得到一个握手异常。如果我去掉设置连接管理器的那一行,它就会工作。这对我来说毫无意义。我正在使用这些jar文件: httpclient-4.5.2.jar httpcore-4.4.4.jar 连接池就绪后: RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(10000) .setConnectionReques
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(5000)
.build();
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(readStore(), KEYSTOREPASS)
.build();
HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.setDefaultRequestConfig(requestConfig)
.setSSLContext(sslContext)
.build();
抛出收到致命警报:握手失败异常:
main, RECV TLSv1.2 ALERT: fatal, handshake_failure
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
%% Invalidated: [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, called close()
main, called closeInternal(true)
00:22:51,523 ERROR TestHttps:155 - Received fatal alert: handshake_failure
已注释掉连接池:
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(5000)
.build();
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(readStore(), KEYSTOREPASS) // use null as second param if you don't have a separate key password
.build();
HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom()
//.setConnectionManager(poolingConnManager)
.setDefaultRequestConfig(requestConfig)
.setSSLContext(sslContext)
.build();
它成功运行并返回我的值(此处已混淆):
我做错了什么?谢谢我找到了这个文档,它是httpconnection manager的标准实现 ** *此连接管理器实现应在EJB容器中使用。可能是因为这个原因,你在main方法中使用了这个** HTTP连接管理器 2.3.1. 托管连接和连接管理器 HTTP连接是复杂的、有状态的、线程不安全的对象,需要正确管理才能正常工作。HTTP连接一次只能由一个执行线程使用。HttpClient使用一个称为HTTP连接管理器的特殊实体来管理对HTTP连接的访问,该实体由HttpClientConnectionManager接口表示。HTTP连接管理器的目的是充当新HTTP连接的工厂,管理持久连接的生命周期,并同步对持久连接的访问,确保一次只有一个线程可以访问一个连接。在内部,HTTP连接管理器使用ManagedHttpClientConnection的实例作为实际连接的代理来管理连接状态并控制I/O操作的执行。如果托管连接被其使用者释放或显式关闭,则底层连接将与其代理分离,并返回给管理器。即使服务使用者仍然持有对代理实例的引用,它也不再能够执行任何I/O操作或有意或无意地更改实际连接的状态 这是从连接管理器获取连接的示例:
HttpClientContext context = HttpClientContext.create();
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
// Request new connection. This can be a long process
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
// Wait for connection up to 10 sec
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
try {
// If not open
if (!conn.isOpen()) {
// establish connection based on its route info
connMrg.connect(conn, route, 1000, context);
// and mark it as route complete
connMrg.routeComplete(conn, route, context);
}
// Do useful things with the connection.
} finally {
connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
}
如有必要,可通过调用ConnectionRequest#cancel()提前终止连接请求。这将解锁ConnectionRequest#get()方法中阻塞的线程
2.3.2。简单连接管理器
BasicHttpClientConnectionManager是一个简单的连接管理器,一次只维护一个连接。即使这个类是线程安全的,它也应该只被一个执行线程使用。BasicHttpClientConnectionManager将努力为具有相同路由的后续请求重用连接。但是,如果持久连接的路由与连接请求的路由不匹配,它将关闭现有连接并为给定路由重新打开它。如果已经分配了连接,则会抛出java.lang.IllegalStateException
**
**
可能是你在主方法中使用这个,因为它是
制造问题
**
*此连接管理器实现应在EJB容器中使用**
*
2.3.3。池连接管理器
PoolighttpClientConnectionManager是一个更复杂的实现,它管理客户端连接池,并能够为来自多个执行线程的连接请求提供服务。连接以每条路由为基础进行汇集。对于管理器在池中已经有可用的持久连接的路由请求,将通过从池中租用连接而不是创建全新的连接来提供服务
PoolighttpClientConnectionManager在每个路由的基础上和总连接数上保持最大连接限制。默认情况下,此实现将为每个给定路由创建不超过2个并发连接,总共不超过20个连接。对于许多实际应用程序来说,这些限制可能会被证明过于严格,尤其是当它们使用HTTP作为服务的传输协议时
此示例显示了如何调整连接池参数:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
2.3.4。连接管理器关闭
当不再需要HttpClient实例并且该实例即将超出范围时,关闭其连接管理器以确保管理器保持活动状态的所有连接都被关闭,并释放由这些连接分配的系统资源是很重要的
CloseableHttpClient httpClient = <...>
httpClient.close();
2.5。连接逐出策略
经典阻塞I/O模型的一个主要缺点是,只有在I/O操作中阻塞时,网络套接字才能对I/O事件作出反应。当连接被释放回管理器时,它可以保持活动状态,但是它无法监视套接字的状态并对任何I/O事件作出反应。如果连接在服务器端关闭,客户端连接将无法检测到连接状态的变化(并通过关闭其端的套接字做出适当的反应)
HttpClient试图通过测试连接是否“过时”来缓解问题,因为在使用连接执行HTTP请求之前,该连接已在服务器端关闭,因此不再有效。陈旧连接检查不是100%可靠。对于空闲连接,唯一不涉及每个套接字一个线程模型的可行解决方案是一个专用的监视线程,用于排除由于长时间不活动而被视为过期的连接。监视器线程可以定期调用ClientConnectionManager#closeExpiredConnections()方法来关闭所有过期的连接并从池中逐出关闭的连接。它还可以选择调用ClientConnectionManager#closeIdleConnections()方法来关闭在给定时间段内处于空闲状态的所有连接
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
2.6。连接保持策略
HTTP规范没有指定h
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
// URIs to perform GETs on
String[] urisToGet = {
"http://www.domain1.com/",
"http://www.domain2.com/",
"http://www.domain3.com/",
"http://www.domain4.com/"
};
// create a thread for each URI
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new GetThread(httpClient, httpget);
}
// start the threads
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
// join the threads
for (int j = 0; j < threads.length; j++) {
threads[j].join();
}
static class GetThread extends Thread {
private final CloseableHttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
this.httpClient = httpClient;
this.context = HttpClientContext.create();
this.httpget = httpget;
}
@Override
public void run() {
try {
CloseableHttpResponse response = httpClient.execute(
httpget, context);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
} catch (ClientProtocolException ex) {
// Handle protocol errors
} catch (IOException ex) {
// Handle I/O errors
}
}
}
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
HttpClientContext.HTTP_TARGET_HOST);
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
// Keep alive for 5 seconds only
return 5 * 1000;
} else {
// otherwise keep alive for 30 seconds
return 30 * 1000;
}
}
};
CloseableHttpClient client = HttpClients.custom()
.setKeepAliveStrategy(myStrategy)
.build();
HttpClientContext clientContext = HttpClientContext.create();
PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory();
Socket socket = sf.createSocket(clientContext);
int timeout = 1000; //ms
HttpHost target = new HttpHost("localhost");
InetSocketAddress remoteAddress = new InetSocketAddress(
InetAddress.getByAddress(new byte[] {127,0,0,1}), 80);
sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);
ConnectionSocketFactory plainsf = <...>
LayeredConnectionSocketFactory sslsf = <...>
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
HttpClients.custom()
.setConnectionManager(cm)
.build();
KeyStore myTrustStore = <...>
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(myTrustStore)
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE);
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
HttpHost proxy = new HttpHost("someproxy", 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
ProxySelector.getDefault());
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
HttpRoutePlanner routePlanner = new HttpRoutePlanner() {
public HttpRoute determineRoute(
HttpHost target,
HttpRequest request,
HttpContext context) throws HttpException {
return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
"https".equalsIgnoreCase(target.getSchemeName()));
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
}
}