使用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  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、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 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(每个线程一个)。

这里有个测试:http://java.dzone.com/articles/synchronized-vs-lock ,在我本机(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 一个没有的问题所在。

如有问题,请赐教。

时间: 2024-10-07 18:32:59

使用httpclient必须知道的参数设置及代码写法、存在的风险的相关文章

阿里云RDS-MYSQL数据库参数设置,K哥

2016.9.2 最近被阿里云的数据库要搞疯掉了 自打阿里云抽风,非要取消myisam引擎,都换成innodb 没事总是主备切换,也没有错误日志 一问客服就是物理机波动,擦,波动是什么???????? 服务器自己跳舞了吗 看了看参数设置,很多都不知道 这两天有时间自己搜索整理了下 发给大家,有需要的看看 我的服务器应用主要是WEB网站服务 有一些不懂的地方或者不对的地方,还请大牛不吝赐教! 回复在评论中就可以了,thank you 我是K哥 auto_increment_offset表示自增长字

Oracle shared_pool_reserved_size参数设置说明

进入SQLPLUS,查询shared_pool,往往看到结果如下: SQL> show parameter shared_pool NAME                                    TYPE              VALUE------------------------------------ ----------- ------------------------------shared_pool_reserved_size           big in

Socket中的异常和参数设置

1.常见异常 1.java.net.SocketTimeoutException . 这个异 常比较常见,socket 超时.一般有 2 个地方会抛出这个,一个是 connect 的 时 候 , 这 个 超 时 参 数 由connect(SocketAddress endpoint,int timeout) 中的后者来决定,还有就是 setSoTimeout(int timeout),这个是设定读取的超时时间.它们设置成 0 均表示无限大. 2.java.net.BindException:Ad

基于V4L2 MFC 视频编解码 H264参数设置

上篇博文硬件视频编码,在默认参数设置后,编码1000帧640x480 H264文件大小为180m左右,非常大,必须设置参数 测试发现再使能EnableMBRateControl后,编码数据才开始大幅缩小: 这里先将54个控制参数全部初始化 -1,在这54个参数中,包含H264,H263和MPEG视频格式的设置,对每个视频格式仅仅选择对应他的参数即可.这里仅仅测试H264格式 所有控制参数如下: struct { int num; int id; char *name; int value; }

C3p0的参数设置

C3p0的参数设置:ComboPooledDataSource和BasicDataSource一样提供了一个用于关闭数据源的close()方法,这样我们就可以保证Spring容器关闭时数据源能够成功释放. C3P0拥有比DBCP更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制: acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目: acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30: acqu

调用enqueue执行异步请求有一个参数设置回调。

前面的内容介绍了OkHttp,本章就来教大家okhttp的基本使用,学了这些基本东西之后,大家有其他的需求可以自行扩展.以下的所有请求都是异步请求服务器,在真实的企业开发中,基本都是异步. 首先创建一个request对象,通过request设置请求url,通过这个类还可以设置更多的请求信息.然后通过Request去构造一个Call对象. 调用enqueue执行异步请求,有一个参数设置回调.请求成功或者失败会调用Callback接口的onResponse跟onFailure方法,因为这是异步请求,

Hibernate 参数设置一览表

Hibernate 参数设置一览表 属性名 用途 hibernate.dialect 一个Hibernate Dialect类名允许Hibernate针对特定的关系数据库生成优化的SQL. 取值 full.classname.of.Dialect hibernate.show_sql 输出所有SQL语句到控制台. 有一个另外的选择是把org.hibernate.SQL这个log category设为debug. eg. true | false hibernate.format_sql 在log

Hive一些参数设置

在使用union all的时候,系统资源足够的情况下,为了加快hive处理速度,可以设置如下参数实现并发执行 set mapred.job.priority=VERY_HIGH; set hive.exec.parallel=true; 设置map reduce个数 -- 设置map capacity set mapred.job.map.capacity=2000; set mapred.job.reduce.capacity=2000; -- 设置每个reduce的大小 set hive.e

JVM常用参数设置

-server -Xms8g 初始堆大小 -Xmx8g 最大堆大小 -Xmn2g 年轻代大小 -Xss1024K 每个线程的堆栈大小 -XX:PermSize=256m -XX:MaxPermSize=512m Perm不属于堆内存,由虚拟机直接分配,可通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小 设置持久代(perm gen)初始值   设置持久代最大值 -XX:ParallelGCThreads=8 并行收集器的线程数 -XX:+UseConcMarkSwe