使用httpclient必须知道的参数设置及代码写法、存在的风险
结论:
如果使用httpclient 3.1并发量比较大的项目,最好升级到httpclient4.2.3上,保证并发量大时能抗住。httpclient 4.3.3,目前还有一些bug;还是用4.2.x稳定版本吧。
以库存项目为例:
httpclient一天并发量在1500w左右,峰值一秒7万。
在之前使用过程中,一直存在大量的
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
at org. apache. http. impl. conn. PoolingClientConnectionManager. leaseConnection( PoolingClientConnectionManager. java: 232)
at org. apache. http. impl. conn. PoolingClientConnectionManager$ 1. getConnection( PoolingClientConnectionManager. java: 199)
at org. apache. http. impl. client. DefaultRequestDirector. execute( DefaultRequestDirector. java: 456)
另外通过jstack查看线程,会发现:
“pool-21-thread-3″ prio=10 tid=0x00007f6b7c002800 nid=0x40ff waiting on condition [0x00007f6b37020000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f97918b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkUntil(LockSupport.java:239)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitUntil(AbstractQueuedSynchronizer.java:2072)
at org.apache.http.pool.PoolEntryFuture.await(PoolEntryFuture.java:129)
at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:281)
at org.apache.http.pool.AbstractConnPool.access$000(AbstractConnPool.java:62)
at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:176)
at org.apache.http.pool.AbstractConnPool$2.getPoolEntry(AbstractConnPool.java:172)
at org.apache.http.pool.PoolEntryFuture.get(PoolEntryFuture.java:100)
at org.apache.http.impl.conn.PoolingClientConnectionManager.leaseConnection(PoolingClientConnectionManager.java:212)
问题:
因为使用了连接池,但连接不够用,造成大量的等待;而且这种等待都有滚雪球的效应(和交易组最近使用的apache common dbcp存在的风险是类似的)。
解决方案
最终我们定了一些合理的参数值,目前来看还没有遇到问题。
思考
其实出问题的原因是我们对一些参数不了解,随意设置其值,不出现问题则好,出现问题很难排查到原因,因此我把使用httpclient必须设置的参数及代码写法及排查方法总结一下,供参考。
参数设置
1、httpclient 4.2.3
此处解释下MaxtTotal和DefaultMaxPerRoute的区别:
1、MaxtTotal是整个池子的大小;
2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只连接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
而我连接到http://sishuok.com 和 http://qq.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400);所以起作用的设置是DefaultMaxPerRoute。
2、httpclient 3.1
参数类似 就不多解释了;
代码写法
1、
httpclient 4.2.3
HttpResponse response = null;
HttpEntity entity = null;
try {
HttpGet get = new HttpGet();
String url = "http://hc.apache.org/";
get.setURI(new URI(url));
response = getHttpClient().execute(get);
/ /处理响应
} catch (Exception e) {
//处理异常
} finally {
if(response != null) {
EntityUtils.consume(response.getEntity()); //会自动释放连接
}
//如下方法也是可以的,但是存在一些风险;不要用
//InputStream is = response.getEntity().getContent();
//is.close();
}
2、
httpclient 3.1
PostMethod postMethod = new PostMethod(yxUrl);
try {
httpClient.executeMethod(postMethod);
} catch (Exception e) {
//处理异常
} finally {
if(postMethod != null) { //不要忘记释放,尽量通过该方法实现,
postMethod.releaseConnection();
//存在风险,不要用
//postMethod.setParameter("Connection", "close");
//InputStream is = postMethod.getResponseBodyAsStream();
//is.clsoe();也会关闭并释放连接的
}
}
存在的风险
1、httpclient 4.2.3 在释放连接时
if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { //如果连接打开的且不可重用(not keepalive) close socket
try {
managedConn.shutdown();
} catch (IOException iox) {
if (this.log.isDebugEnabled()) {
this.log.debug("I/O exception shutting down released connection", iox);
}
}
}
// Only reusable connections can be kept alive
if (managedConn.isMarkedReusable()) {
entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
if (this.log.isDebugEnabled()) {
String s;
if (keepalive > 0) {
s = "for " + keepalive + " " + tunit;
} else {
s = "indefinitely";
}
this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
}
}
2、httpclient 3.1
也就是说如果走http1.1且没有设置相关参数;那么socket其实是没有关闭的;可能造成很多TIME_WAIT;因此如果是走短连接建议设置postMethod.setParameter(“Connection”, “close”)。
其他注意事项:
httpclient 3.1 使用synchronized+wait+notifyAll,存在两个问题,量大synchronized慢和notifyAll可能造成线程饥饿;httpclient 4.2.3 使用 ReentrantLock(默认非公平) + Condition(每个线程一个)。
这里有个测试: http://java.dzone.com/articles/synchronized-vs-lock ,在我本机(jdk1.6.0_43 )测试结果明细锁的优势比较大
1x synchronized {} with 32 threads took 2.621 seconds
这也是为什么在库存项目中使用httpclient 3.1 依然有大量的wait,而httpclient4.2.3 一个没有的问题所在。
如有问题,请赐教。