httpClient 连接池问题出现403.9
困扰了半个月时间终于找到连接池的问题,由于调用第三方有异常导致连接不能及时释放 所以写了一个定时扫描释放连接
监控连接池释放连接:
public static class IdleConnectionEvictor extends Thread { private final HttpClientConnectionManager connectionManager; private volatile boolean shutdown; public IdleConnectionEvictor(HttpClientConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
//3s检查一次
wait(2000);
// 关闭失效的连接
connectionManager.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
//连接超过30秒空闲释放连接
connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// 结束
ex.printStackTrace();
}
} public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
参照官方文档:
https://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html
第二章。连接管理
2.1。连接的持久性
建立连接的过程从一个宿主传播到另一个相当复杂,涉及多个包两个端点之间的交流,这可能非常耗时。连接握手的开销会很大,特别是对于小HTTP消息。可以达到一个更高的数据吞吐量如果打开的连接可以执行多个请求重用。
HTTP / 1.1,HTTP连接可以重复用于多个请求/违约。HTTP / 1.0兼容端点也可以使用一种机制来明确他们的偏好进行沟通,保持连接活着,使用多个请求。HTTP代理还可以保持一段时间空闲连接在连接到同一个目标主机需要后续的请求。保持联系的能力通常是指连接持久性。HttpClient完全支持连接的持久性。
2.2。HTTP连接路由
HttpClient能够建立连接到目标主机直接或通过一个路线,可能涉及多个中间连接——也称为啤酒花。HttpClient区分线路连接成平原,隧道和分层。使用多个中介代理隧道连接到目标主机被称为代理链接。
普通路线建立了连接到目标或第一个也��唯一一个代理。隧道线路建立连接到第一个和隧道通过一连串的代理到目标。没有代理不能挖过的路线。分层路由建立了分层协议在一个现有的连接。协议只能在隧道到目标分层,通过直接连接或没有代理。
2.2.1。路径计算
的 RouteInfo
接口代表信息明确的路由到目标主机包括一个或多个中间步骤或跳。 HttpRoute
具体实现的吗 RouteInfo
不能改变(不可变的)。 HttpTracker
是一个可变的 RouteInfo
实现内部使用HttpClient跟踪剩下的啤酒花的终极目标。 HttpTracker
可以更新成功后执行下一跳向目标的路线。 HttpRouteDirector
是一个辅助类,它可以用来计算路线的下一步。这门课由HttpClient内部使用。
HttpRoutePlanner
是一个接口代表一��策略来计算一个完整的路线到一个给定的目标基于执行上下文。HttpClient附带两个默认 HttpRoutePlanner
实现。 SystemDefaultRoutePlanner
是基于 java.net.ProxySelector
。默认情况下,它将JVM的代理设置,从系统属性或从浏览器中运行应用程序。的 DefaultProxyRoutePlanner
实现不使用任何Java系统属性,也没有任何系统或浏览器代理设置。它总是通过相同的默认代理计算路线。
2.2.2。安全的HTTP连接
HTTP连接可以被认为是安全的,如果信息传播的两个连接端点之间无法读取或篡改的未经授权的第三方。SSL / TLS协议是应用最广泛的技术,以确保HTTP传输安全。然而,其他加密技术可以使用。通常,HTTP传输是分层的SSL / TLS加密连接。
2.3。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。池连接管理器
PoolingHttpClientConnectionManager
是一个更复杂的实现管理的客户端连接,能够从多个执行线程服务连接请求。连接池在每个路由的基础上。请求路由的经理已经持久连接池中可用的将由租赁服务连接池而不是创建一个新的连接。
PoolingHttpClientConnectionManager
保持最大限度的连接在每个路线的基础上,。默认该实现将创建不超过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.4。多线程的请求执行
当配备一个池连接管理器等 PoolingClientConnectionManager
,可以使用HttpClient执行多个请求同时使用多个线程的执行。
的 PoolingClientConnectionManager
将根据其分配连接配置。如果所有连接对于一个给定的路线已经被租用,请求连接将阻塞,直到连接释放回池中。可以确保连接管理器不会无限期块连接请求的操作设置 'http.conn-manager.timeout'
一个积极的价值。如果连接请求不能在给定的时间内提供服务 ConnectionPoolTimeoutException
将抛出。
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();
}
而 HttpClient
实例是线程安全的,可以执行多个线程之间共享,强烈建议每个线程维护自己的专用的实例 HttpContext
.
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
}
} }
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规范没有指定多长时间持久连接可能,应该保留。一些HTTP服务器使用非标准 Keep-Alive
头给客户端通信的时间以秒他们打算活着在服务器端保持连接。HttpClient利用这些信息,如果可用。如果 Keep-Alive
标题中不存在的反应,HttpClient假设连接能够存活下去。然而,许多HTTP服务器配置一般使用持久连接在某一静止期下降为了节约系统资源,经常没有通知客户端。如果默认策略被证明过于乐观,一个可能需要提供一个自定义维生的策略。
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();
2.7。连接套接字工厂
利用HTTP连接 java.net.Socket
对象内部处理跨线的传输数据。然而他们依赖 ConnectionSocketFactory
接口来创建、初始化和连接套接字。这使得HttpClient的用户提供特定于应用程序的套接字在运行时初始化代码。 PlainConnectionSocketFactory
工厂是默认为创建和初始化(加密)套接字。
创建一个套接字的过程和连接到主机是解耦的,这样可以关闭套接字被屏蔽在连接操作。
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);
2.7.1。安全套接字层
LayeredConnectionSocketFactory
是一个扩展的 ConnectionSocketFactory
接口。分层的套接字工厂可以创建套接字层在现有普通插座。套接字层主要用于创建通过代理安全套接字。HttpClient附带 SSLSocketFactory
实现SSL / TLS分层。请注意HttpClient不使用任何自定义加密功能。它完全依赖于标准的Java加密(JCE)和安全套接字(JSEE)扩展。
2.7.2。集成连接管理器
自定义连接套接字工厂可以关联到一个特定的协议方案作为HTTP或HTTPS然后用于创建一个定制的连接管理器。
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();
2.7.3。SSL / TLS定制
HttpClient利用 SSLConnectionSocketFactory
建立SSL连接。 SSLConnectionSocketFactory
允许一个高程度的定制。它可以采取的一个实例 javax.net.ssl.SSLContext
作为一个参数,并使用它来创建自定义配置SSL连接。
KeyStore myTrustStore = <...>
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(myTrustStore)
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
定制的 SSLConnectionSocketFactory
意味着一定程度的熟悉的SSL / TLS协议的概念,详细解释这是超出了本文的范围。请参考Java™安全套接字扩展(JSSE)参考指南的详细描述 javax.net.ssl.SSLContext
和相关的工具。
第2.7.4。主机名验证
除了信任验证和客户端身份验证SSL / TLS协议级别上执行,HttpClient可以验证目标主机是否匹配名称存储在服务器的X。509证书,一旦连接已经建立。这个验证可以提供额外的服务器的信任的真实性保证材料。的 javax.net.ssl.HostnameVerifier
接口代表主机名验证的策略。HttpClient附带两个 javax.net.ssl.HostnameVerifier
实现。重要:主机名验证SSL信任验证不应混淆。
DefaultHostnameVerifier
: 默认实现使用HttpClient有望符合RFC 2818。主机名必须匹配任何的替代名称指定的证书,或者如果没有替代的名字有证书的最具体的CN主题。一个通配符可以发生在CN,任何subject-alts。NoopHostnameVerifier
: 这个主机名验证器本质上是主机名验证。它接受任何SSL会话有效,匹配的目标主机。
每违约HttpClient使用 DefaultHostnameVerifier
实现。你可以指定一个不同的主机名如果需要验证器实现
SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE);
HttpClient使用4.4版本的公共后缀列表请由Mozilla基金会以确保通配符在SSL证书不能滥用申请多个域的通用顶级域名。HttpClient附带一份列表中检索时的释放。最新修订版本的列表可以发现https://publicsuffix.org/list/。是高度adviseable列表和下载列表的一个本地副本每天不超过一次从其原始位置。
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
可以禁用验证对公众suffic通��使用列表 null
匹配器。
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
2.8。HttpClient代理配置
尽管HttpClient意识到复杂的路由方案和代理链接,它只支持简单的直接或一个跳代理连接的。
最简单的方法告诉HttpClient连接到目标主机通过代理是通过设置默认代理参数:
HttpHost proxy = new HttpHost("someproxy", 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
人们还可以指示HttpClient使用标准的JRE代理选择器来获取代理信息:
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
ProxySelector.getDefault());
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
或者,你可以提供一个自定义的 RoutePlanner
为了有一个完整的控制实现的过程HTTP路径计算:
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();
}
}
httpClient 连接池问题出现403.9的更多相关文章
- Http持久连接与HttpClient连接池
一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...
- Http 持久连接与 HttpClient 连接池
一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...
- HttpClient连接池
HttpClient连接池,发现对于高并发的请求,效率提升很大.虽然知道是因为建立了长连接,导致请求效率提升,但是对于内部的原理还是不太清楚.后来在网上看到了HTTP协议的发展史,里面提到了一个属性C ...
- HttpClient连接池的一些思考
前言 使用apache的httpclient进行http的交互处理已经很长时间了,而httpclient实例则使用了http连接池,想必大家也没有关心过连接池的管理.事实上,通过分析httpclien ...
- HttpClient实战三:Spring整合HttpClient连接池
简介 在微服务架构或者REST API项目中,使用Spring管理Bean是很常见的,在项目中HttpClient使用的一种最常见方式就是:使用Spring容器XML配置方式代替Java编码方式进行H ...
- httpclient连接池在ES Restful API请求中的应用
package com.wm.utils; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http ...
- springboot使用RestTemplate+httpclient连接池发送http消息
简介 RestTemplate是spring支持的一个请求http rest服务的模板对象,性质上有点像jdbcTemplate RestTemplate底层还是使用的httpclient(org.a ...
- 使用HttpClient连接池进行https单双向验证
https单双向验证环境的搭建参见:http://www.cnblogs.com/YDDMAX/p/5368404.html 一.单向握手 示例程序: package com.ydd.study.he ...
- HttpClient连接池的连接保持、超时和失效机制
HTTP是一种无连接的事务协议,底层使用的还是TCP,连接池复用的就是TCP连接,目的就是在一个TCP连接上进行多次的HTTP请求从而提高性能.每次HTTP请求结束的时候,HttpClient会判断连 ...
随机推荐
- 终端直接执行py文件,不需要python命令
然后给脚本文件运行权限,方法(1)chmod +x ./*.py方法(2)chmod 755 ./*.py (777也无所谓啦) 这个命令不去调整,会出现permission denied的错误终端直 ...
- Java中的yield关键字的简单讲解
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程. yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会.因此,使用yie ...
- 使用Golang进行性能分析(Profiling)
转自:http://www.cppblog.com/sunicdavy/archive/2015/04/11/210308.html 本文介绍游戏服务器的性能分析, web服务器性能分析不在本文分析范 ...
- CSS宽度高度的百分比取值基于谁
width=num% , height=num% 基于以下几点 1. 若元素不存在定位: 则基于直接父元素的宽高度 2. 若元素存在定位 且 定位为 relative, 则也基于直接父元素的宽高度 3 ...
- json , 正则
json: import json user = { 'dsada': 'whichT','a':True,'b':None } a=json.dumps(user,indent=3,sort_key ...
- hive xml udf
<store> <book id="book"><title id="titile">hive</title> ...
- 非常简单的 xml转成数组的方法
function xml2arr($xml){ $obj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); $jso ...
- Spring @Trasactionl 失效, JDK,CGLIB动态代理
@Transaction: http://blog.csdn.net/bao19901210/article/details/41724355 Spring上下文: http://blog.csd ...
- 5.15 python 面向对象的软件开发&领域模型
1.面向对象的软件开发 参考地址::http://www.cnblogs.com/linhaifeng/articles/6182264.html#_label14 面向对象的软件工程包括下面几个部: ...
- jqGrid 使用案例及笔记
jqGrid 是一个用来显示网格数据的jQuery插件,通过使用jqGrid可以轻松实现前端页面与后台数据的ajax异步通信. 一.要引用的文件 要使用jqGrid,首先页面上要引入如下css与js文 ...