博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用httpclient必须知道的参数设置及代码写法、存在的风险
阅读量:6284 次
发布时间:2019-06-22

本文共 7384 字,大约阅读时间需要 24 分钟。

hot3.png

原文出处: 

结论:

如果使用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

atorg.apache.http.impl.conn.PoolingClientConnectionManager.leaseConnection(PoolingClientConnectionManager.java:232)
atorg.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

HttpParams params = new BasicHttpParams();

//设置连接超时时间

Integer CONNECTION_TIMEOUT = 2 * 1000; //设置请求超时2秒钟 根据业务调整

Integer SO_TIMEOUT = 2 * 1000; //设置等待数据超时时间2秒钟 根据业务调整

//定义了当从ClientConnectionManager中检索ManagedClientConnection实例时使用的毫秒级的超时时间

//这个参数期望得到一个java.lang.Long类型的值。如果这个参数没有被设置,默认等于CONNECTION_TIMEOUT,因此一定要设置

Long CONN_MANAGER_TIMEOUT = 500L; //该值就是连接不够用的时候等待超时时间,一定要设置,而且不能太大 ()

params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT);

params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SO_TIMEOUT);

params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, CONN_MANAGER_TIMEOUT);

//在提交请求之前 测试连接是否可用

params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true);

PoolingClientConnectionManager conMgr = new PoolingClientConnectionManager();

conMgr.setMaxTotal(200); //设置整个连接池最大连接数 根据自己的场景决定

//是路由的默认最大连接(该值默认为2),限制数量实际使用DefaultMaxPerRoute并非MaxTotal。

//设置过小无法支持大并发(ConnectionPoolTimeoutException: Timeout waiting for connection from pool),路由是对maxTotal的细分。

conMgr.setDefaultMaxPerRoute(conMgr.getMaxTotal());//(目前只有一个路由,因此让他等于最大值)

//另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)

httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));

此处解释下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

HttpConnectionManagerParams params = new HttpConnectionManagerParams();

params.setConnectionTimeout(2000);

params.setSoTimeout(2000);

// 最大连接数

params.setMaxTotalConnections(500);

params.setDefaultMaxConnectionsPerHost(500);

params.setStaleCheckingEnabled(true);

connectionManager.setParams(params);

HttpClientParams httpClientParams = new HttpClientParams();

// 设置httpClient的连接超时,对连接管理器设置的连接超时是无用的

httpClientParams.setConnectionManagerTimeout(5000); //等价于4.2.3中的CONN_MANAGER_TIMEOUT

httpClient = new HttpClient(connectionManager);

httpClient.setParams(httpClientParams);

//另外设置http client的重试次数,默认是3次;当前是禁用掉(如果项目量不到,这个默认即可)

httpClientParams.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));

参数类似 就不多解释了;

代码写法

1、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 在释放连接时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1、如果走http1.1协议:如果proxy-connection/connection请求头设置为close;那么会关闭socket; 或者这两个头不等于close 也会自动关;

2、如果是keep-alive ,不会关闭;

3、如果协议小于等于http1.0协议没有问题;调用releaseConnection时会close socket;

4、其他情况不会close;

也就是说如果走http1.1且没有设置相关参数;那么socket其实是没有关闭的;可能造成很多TIME_WAIT;因此如果是走短连接建议设置postMethod.setParameter(“Connection”, “close”)。

其他注意事项:

1、使用keep-alive一定要设置Content-Length头(否则也不是长连接)。

2、在使用httpclient3.1时(4.2.3没问题);尽量不要调用 byte[] getResponseBody() :因为如果Content-Length没设置或者传输的数据大于1M,会有大量如下日志

LOG.warn(“Going to buffer response body of large or unknown size. “

+”Using getResponseBodyAsStream instead is recommended.”);

如果大于1M可以设置该参数;但是-1的话就没办法了,就不要调用 byte[] getResponseBody()

httpClientParams.setLongParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 2L * 1024 * 1024);

3、锁

httpclient 3.1 使用synchronized+wait+notifyAll,存在两个问题,量大synchronized慢和notifyAll可能造成线程饥饿;httpclient 4.2.3 使用 ReentrantLock(默认非公平) + Condition(每个线程一个)。

这里有个测试: ,在我本机(jdk1.6.0_43 )测试结果明细锁的优势比较大

1x synchronized {} with 32 threads took 2.621 seconds

1x Lock.lock()/unlock() with 32 threads took 1.951 seconds

1x AtomicInteger with 32 threads took 4.113 seconds

1x synchronized {} with 64 threads took 2.621 seconds

1x Lock.lock()/unlock() with 64 threads took 1.983 seconds

这也是为什么在库存项目中使用httpclient 3.1 依然有大量的wait,而httpclient4.2.3 一个没有的问题所在。

问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条 官方网站:www.wenaaa.com 下载问啊APP,参与官方悬赏,赚百元现金。

QQ群290551701 聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!

转载于:https://my.oschina.net/u/2394328/blog/647424

你可能感兴趣的文章
Mono for Android 优势与劣势
查看>>
将图片转成base64字符串并在JSP页面显示的Java代码
查看>>
js 面试题
查看>>
sqoop数据迁移(基于Hadoop和关系数据库服务器之间传送数据)
查看>>
腾讯云下安装 nodejs + 实现 Nginx 反向代理
查看>>
Javascript 中的 Array 操作
查看>>
java中包容易出现的错误及权限问题
查看>>
AngularJS之初级Route【一】(六)
查看>>
服务器硬件问题整理的一点总结
查看>>
SAP S/4HANA Cloud: Revolutionizing the Next Generation of Cloud ERP
查看>>
Mellanox公司计划利用系统芯片提升存储产品速度
查看>>
白帽子守护网络安全,高薪酬成大学生就业首选!
查看>>
ARM想将芯片装进人类大脑 降低能耗是一大挑战
查看>>
Oracle数据库的备份方法
查看>>
Selenium 自动登录考勤系统
查看>>
关于如何以编程的方式执行TestNG
查看>>
智能照明造福千家万户 家居智能不再是梦
查看>>
物联网如何跳出“看起来很美”?
查看>>
浅谈MySQL 数据库性能优化
查看>>
《UNIX/Linux 系统管理技术手册(第四版)》——1.10 其他的权威文档
查看>>