转载请注明出处,谢谢~

http://blog.csdn.net/shootyou/archive/2011/05/12/6415248.aspx

在一次服务器异常的排查过程当中(服务器异常排查的过程我会另起文章),我们决定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。

为什么使用HttpClient4?主要是HttpConnection没有连接池的概念,多少次请求就会建立多少个IO,在访问量巨大的情况下服务器的IO可能会耗尽。

HttpClient3也有连接池的东西在里头,使用MultiThreadedHttpConnectionManager,大致过程如下:

  1. MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
  2. HttpClient client = new HttpClient(connectionManager);...// 在某个线程中。
  3. GetMethod get = new GetMethod("http://jakarta.apache.org/");
  4. try {
  5. client.executeMethod(get);// print response to stdout
  6. System.out.println(get.getResponseBodyAsStream());
  7. } finally {
  8. // be sure the connection is released back to the connection
  9. managerget.releaseConnection();
  10. }

可以看出来,它的方式与jdbc连接池的使用方式相近,我觉得比较不爽的就是需要手动调用releaseConnection去释放连接。对每一个HttpClient.executeMethod须有一个method.releaseConnection()与之匹配。

HttpClient4在这点上做了改进,使用我们常用的InputStream.close()来确认连接关闭(4.1版本之前使用entity.consumeContent()来确认内容已经被消耗关闭连接)。具体方式如下:

  1. ...HttpClient client = null;InputStream in = null;
  2. try{
  3. client = HttpConnectionManager.getHttpClient();
  4. HttpGet get = new HttpGet();
  5. get.setURI(new URI(urlPath));
  6. HttpResponse response = client.execute(get);
  7. HttpEntity entity =response.getEntity();
  8. if( entity != null ){
  9. in = entity.getContent();
  10. ....
  11. }catch (Exception e){
  12. ....
  13. }finally{
  14. if (in != null){
  15. try{in.close ();}catch (IOException e){
  16. e.printStackTrace ();
  17. }
  18. }
  19. }

2012-03-06更新:

有网友提出调用in.close()是否会关闭底层socket,事情是这样的:

  1. 回复kangkang203:感谢你提出的这个问题。

  2. 先我文中提出的方法in.close()它会触发一个连接的释放这个连接将重新被连接管理器收回,官网的原文是这么说
    的:“Closing the input stream will trigger connection release...the underlying connection gets released back to the connection manager”。
    但是底层的socket是否会被关闭是不一定的,我看了部分源码(EofSensorInputStream)发现,大多数情况socket并不会关闭,
    而是否关闭socket貌似是由一个Watcher去决定的。所以in.close的调用不会引起socket的关闭。
  3. 另外,由于http本身我们把它当做“短连接”,所以在一次请求交互完成后仍然打开socket的意义不是很大,毕竟它不像长连接那样在一个连接建立之后会有很多次数据交互。我们试用连接管理器的更多意义在于它对连接的管理。

好说完了连接池的使用流程,现在来说一说连接池在使用时最重要的几个参数。我用4.1的版本实现了一个简单的HttpConnectionManager,代码如下:

  1. public class HttpConnectionManager {
  2. private static HttpParams httpParams;
  3. private static ClientConnectionManager connectionManager;
  4. /**
  5. * 最大连接数
  6. */
  7. public final static int MAX_TOTAL_CONNECTIONS = 800;
  8. /**
  9. * 获取连接的最大等待时间
  10. */
  11. public final static int WAIT_TIMEOUT = 60000;
  12. /**
  13. * 每个路由最大连接数
  14. */
  15. public final static int MAX_ROUTE_CONNECTIONS = 400;
  16. /**
  17. * 连接超时时间
  18. */
  19. public final static int CONNECT_TIMEOUT = 10000;
  20. /**
  21. * 读取超时时间
  22. */
  23. public final static int READ_TIMEOUT = 10000;
  24. static {
  25. httpParams = new BasicHttpParams();
  26. // 设置最大连接数
  27. ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);
  28. // 设置获取连接的最大等待时间
  29. ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);
  30. // 设置每个路由最大连接数
  31. ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
  32. ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);
  33. // 设置连接超时时间
  34. HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
  35. // 设置读取超时时间
  36. HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);
  37. SchemeRegistry registry = new SchemeRegistry();
  38. registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
  39. registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
  40. connectionManager = new ThreadSafeClientConnManager(httpParams, registry);
  41. }
  42. public static HttpClient getHttpClient() {
  43. return new DefaultHttpClient(connectionManager, httpParams);
  44. }
  45. }

最大连接数、获取连接的最大等待时间、读取超时时间 这些配置应该比较容易理解,一般的连接池都会有这些配置,比较特别的是 每个路由(route)最大连接数 。

什么是一个route?

这里route的概念可以理解为 运行环境机器 到 目标机器的一条线路。举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route。

这里为什么要特别提到route最大连接数这个参数呢,因为这个参数的默认值为2,如果
不设置这个参数值默认情况下对于同一个目标机器的最大并发连接只有2个!这意味着如果你正在执行一个针对某一台目标机器的抓取任务的时候,哪怕你设置连接
池的最大连接数为200,但是实际上还是只有2个连接在工作,其他剩余的198个连接都在等待,都是为别的目标机器服务的。

怎么样蛋疼吧,我是已经有过血的教训了,在切换到HttpClient4.1的起初没有注意到这个配置,最后使得服务承受的压力反而不如从前了,所以在这里特别提醒大家注意。

HttpClient4.X 教程下载:

http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient-contrib/docs/translated-tutorial/httpclient-tutorial-simplified-chinese.pdf

关于版本的补充:

网友w2449008821提醒之后我才发现在HttpClient4.1+的版本ConnManagerParams已经被Deprecated了。

我在写这篇日志的时候时候的httpclient 版本是4.0.3,从4.0版本之后ConnManagerParams被Deprecated,没想到一个小版本升级会有这么大变化。

官网教程举例了新的连接池设置:

  1. SchemeRegistry schemeRegistry = new SchemeRegistry();
  2. schemeRegistry.register(
  3. new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
  4. schemeRegistry.register(
  5. new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
  6. ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
  7. // Increase max total connection to 200
  8. cm.setMaxTotalConnections(200);
  9. // Increase default max connection per route to 20
  10. cm.setDefaultMaxPerRoute(20);
  11. // Increase max connections for localhost:80 to 50
  12. HttpHost localhost = new HttpHost("locahost", 80);
  13. cm.setMaxForRoute(new HttpRoute(localhost), 50);
  14. HttpClient httpClient = new DefaultHttpClient(cm);

ConnManagerParams的功能被挪到了 ThreadSafeClientConnManager 和 HttpConnectionParams两个类:

static ConnPerRoute getMaxConnectionsPerRoute(HttpParams params) 
          Deprecated. use ThreadSafeClientConnManager.getMaxForRoute(org.apache.http.conn.routing.HttpRoute)
static int getMaxTotalConnections(HttpParams params) 
          Deprecated. use ThreadSafeClientConnManager.getMaxTotal()
static long getTimeout(HttpParams params) 
          Deprecated. use HttpConnectionParams.getConnectionTimeout(HttpParams)
static void setMaxConnectionsPerRoute(HttpParams params, ConnPerRoute connPerRoute) 
          Deprecated. use ThreadSafeClientConnManager.setMaxForRoute(org.apache.http.conn.routing.HttpRoute,
int)
static void setMaxTotalConnections(HttpParams params,
int maxTotalConnections)
 
          Deprecated. use ThreadSafeClientConnManager.setMaxTotal(int)
static void setTimeout(HttpParams params,
long timeout)
 
          Deprecated. use HttpConnectionParams.setConnectionTimeout(HttpParams,
int)

参考:http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/params/ConnManagerParams.html

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e638

HttpClient4.X 升级 入门 + http连接池使用的更多相关文章

  1. HttpClient连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查

    转自: http://blog.csdn.net/shootyou/article/details/6615051 今天解决了一个HttpClient的异常,汗啊,一个HttpClient使用稍有不慎 ...

  2. JdbcTemplae使用入门&&Spring三种连接池配置&&Spring配置文件引用外部properties文件

    JdbcTemplate的使用 Spring为了各种支持的持久化技术,都提供了简单操作的模版和回调. JdbcTemplate 简化 JDBC 操作HibernateTemplate 简化 Hiber ...

  3. c3p0连接池快速入门

    为什么要使用连接池,这些基本也不用说那么多 以下为快速入门案例 包目录结构 配置文件c3p0-config.xml <c3p0-config> <!-- 默认配置,如果没有指定自己的 ...

  4. 十六 Spring的JDBC模版入门,默认连接池

    Spring是EE开发一站式框架,有EE开发的每层的解决方案,Spring对持久层也提供了解决方案:ORM模块和JDBC的模版

  5. ADO.NET 快速入门(十一):连接池

    这个示例演示了如何构建一个到数据源的连接池.你可以通过连接池部署高性能的应用程序.本例中使用连接串创建连接池,并且由 SqlConnection 自动管理.   string connString; ...

  6. HttpClient4.5.2 连接池原理及注意事项

    随着微服务的流行,服务之间的http调用越来越多,遇到的问题也比较多,写这边文章的目的也是将自己遇到的坑和解决方案跟大家分享 一.为什么要用Http连接池 1.降低延迟:如果不采用连接池,每次连接发起 ...

  7. SpringBoot入门篇--整合mybatis+generator自动生成代码+druid连接池+PageHelper分页插件

    原文链接 我们这一篇博客讲的是如何整合Springboot和Mybatis框架,然后使用generator自动生成mapper,pojo等文件.然后再使用阿里巴巴提供的开源连接池druid,这个连接池 ...

  8. httpclient4.5 连接池的封装

    随着微服务的流行,服务之间的http调用越来越多,遇到的问题也比较多,写这边文章的目的也是将自己遇到的坑和解决方案跟大家分享 一.为什么要用Http连接池 1.降低延迟:如果不采用连接池,每次连接发起 ...

  9. JDBC实例--工具类升级,使用Apache DBCP连接池重构DBUtility,让连接数据库更有效,更安全

    直接使用JDBC访问数据库时,需要避免以下隐患: 1. 每一次数据操作请求都需要建立数据库连接.打开连接.存取数据和关闭连接等步骤.而建立和打开数据库连接是一件既耗资源又费时的过程,如果频繁发生这种数 ...

随机推荐

  1. 【差分约束系统】【spfa】UVALive - 4885 - Task

    差分约束系统讲解看这里:http://blog.csdn.net/xuezhongfenfei/article/details/8685313 模板题,不多说.要注意的一点是!!!对于带有within ...

  2. 调用上一个页面的js方法

    点击商品分类,弹出下框 点击确定,将选中的类别的name和唯一的code返回到上个页面 function save(){ var ids = getIdSelections(); jp.get(&qu ...

  3. NSNotificationCenter监听TextField文字变化

    注册 1: NSNotificationCenter.defaultCenter().addObserver(self, selector: "textDidChange", na ...

  4. iOS开发技巧——关闭Autoresizing开启Autolayout

    在使用Autolayout时有时候会有莫名其妙的错误,是因为UIView是默认同时开启Autoresizing和Autolayout的. 但我们的Autolayout很容易和Autoresizing冲 ...

  5. easyui中一键清空搜索栏搜索条件的思路

    $.fn.clearAllSearchPanel = function () { var $id = $(this); $id.find(".form-control").each ...

  6. SQL Server Wait Types Library

    https://www.sqlskills.com/blogs/paul/announcing-the-comprehensive-sql-server-wait-types-and-latch-cl ...

  7. 查看Java代码对应的汇编指令又一利器,JITWatch 转

    http://www.tuicool.com/articles/IRrIRb3 时间 2015-05-13 08:00:00  Liuxinglanyue's Blog 原文  http://java ...

  8. 手把手教你使用FineUI开发一个b/s结构的取送货管理信息系统系列博文索引

    近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座,来讲解如何利用FineUI快速开 ...

  9. DOM系统学习-表格、样式和元素大小

    操作表格  属性: caption    标题元素 tHead    表头元素 tFoot    表尾元素 tBodies    主体元素集合,通过下标取 row    行元素集合,通过下标取  方法 ...

  10. Swift,循环及判断

    1.for循环(执行固定次数的操作) (1)基本数组循环 var a=[1,2,3] for value in a{ print(value) //1 2 3 } (2)自定义循环次数 for i i ...