HttpClient4.X 升级 入门 + http连接池使用
2016-04-29 15:35
645 查看
转载请注明出处,谢谢~
http://blog.csdn.net/shootyou/archive/2011/05/12/6415248.aspx
在一次服务器异常的排查过程当中(服务器异常排查的过程我会另起文章),我们决定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。
为什么使用HttpClient4?主要是HttpConnection没有连接池的概念,多少次请求就会建立多少个IO,在访问量巨大的情况下服务器的IO可能会耗尽。
HttpClient3也有连接池的东西在里头,使用MultiThreadedHttpConnectionManager,大致过程如下:
[java] view plain copy print?
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);...// 在某个线程中。
GetMethod get = new GetMethod("http://jakarta.apache.org/");
try {
client.executeMethod(get);// print response to stdout
System.out.println(get.getResponseBodyAsStream());
} finally {
// be sure the connection is released back to the connection
managerget.releaseConnection();
}
可以看出来,它的方式与jdbc连接池的使用方式相近,我觉得比较不爽的就是需要手动调用releaseConnection去释放连接。对每一个HttpClient.executeMethod须有一个method.releaseConnection()与之匹配。
HttpClient4在这点上做了改进,使用我们常用的InputStream.close()来确认连接关闭(4.1版本之前使用entity.consumeContent()来确认内容已经被消耗关闭连接)。具体方式如下:
[java] view plain copy print?
...HttpClient client = null;InputStream in = null;
try{
client = HttpConnectionManager.getHttpClient();
HttpGet get = new HttpGet();
get.setURI(new URI(urlPath));
HttpResponse response = client.execute(get);
HttpEntity entity =response.getEntity();
if( entity != null ){
in = entity.getContent();
....
}catch (Exception e){
....
}finally{
if (in != null){
try{in.close ();}catch (IOException e){
e.printStackTrace ();
}
}
}
[plain] view plain copy print?
回复kangkang203:感谢你提出的这个问题。
首 先我文中提出的方法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的关闭。
另外,由于http本身我们把它当做“短连接”,所以在一次请求交互完成后仍然打开socket的意义不是很大,毕竟它不像长连接那样在一个连接建立之后会有很多次数据交互。我们试用连接管理器的更多意义在于它对连接的管理。
好说完了连接池的使用流程,现在来说一说连接池在使用时最重要的几个参数。我用4.1的版本实现了一个简单的HttpConnectionManager,代码如下:
[java] view plain copy print?
public class HttpConnectionManager {
private static HttpParams httpParams;
private static ClientConnectionManager connectionManager;
/**
* 最大连接数
*/
public final static int MAX_TOTAL_CONNECTIONS = 800;
/**
* 获取连接的最大等待时间
*/
public final static int WAIT_TIMEOUT = 60000;
/**
* 每个路由最大连接数
*/
public final static int MAX_ROUTE_CONNECTIONS = 400;
/**
* 连接超时时间
*/
public final static int CONNECT_TIMEOUT = 10000;
/**
* 读取超时时间
*/
public final static int READ_TIMEOUT = 10000;
static {
httpParams = new BasicHttpParams();
// 设置最大连接数
ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);
// 设置获取连接的最大等待时间
ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);
// 设置每个路由最大连接数
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);
// 设置连接超时时间
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
// 设置读取超时时间
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
connectionManager = new ThreadSafeClientConnManager(httpParams, registry);
}
public static HttpClient getHttpClient() {
return new DefaultHttpClient(connectionManager, httpParams);
}
}
最大连接数、获取连接的最大等待时间、读取超时时间 这些配置应该比较容易理解,一般的连接池都会有这些配置,比较特别的是 每个路由(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,没想到一个小版本升级会有这么大变化。
官网教程举例了新的连接池设置:
[java] view plain copy print?
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(
new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
// Increase max total connection to 200
cm.setMaxTotalConnections(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.setMaxForRoute(new HttpRoute(localhost), 50);
HttpClient httpClient = new DefaultHttpClient(cm);
ConnManagerParams的功能被挪到了 ThreadSafeClientConnManager 和 HttpConnectionParams两个类:
参考: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
公司代码:
http://blog.csdn.net/shootyou/archive/2011/05/12/6415248.aspx
在一次服务器异常的排查过程当中(服务器异常排查的过程我会另起文章),我们决定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。
为什么使用HttpClient4?主要是HttpConnection没有连接池的概念,多少次请求就会建立多少个IO,在访问量巨大的情况下服务器的IO可能会耗尽。
HttpClient3也有连接池的东西在里头,使用MultiThreadedHttpConnectionManager,大致过程如下:
[java] view plain copy print?
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);...// 在某个线程中。
GetMethod get = new GetMethod("http://jakarta.apache.org/");
try {
client.executeMethod(get);// print response to stdout
System.out.println(get.getResponseBodyAsStream());
} finally {
// be sure the connection is released back to the connection
managerget.releaseConnection();
}
可以看出来,它的方式与jdbc连接池的使用方式相近,我觉得比较不爽的就是需要手动调用releaseConnection去释放连接。对每一个HttpClient.executeMethod须有一个method.releaseConnection()与之匹配。
HttpClient4在这点上做了改进,使用我们常用的InputStream.close()来确认连接关闭(4.1版本之前使用entity.consumeContent()来确认内容已经被消耗关闭连接)。具体方式如下:
[java] view plain copy print?
...HttpClient client = null;InputStream in = null;
try{
client = HttpConnectionManager.getHttpClient();
HttpGet get = new HttpGet();
get.setURI(new URI(urlPath));
HttpResponse response = client.execute(get);
HttpEntity entity =response.getEntity();
if( entity != null ){
in = entity.getContent();
....
}catch (Exception e){
....
}finally{
if (in != null){
try{in.close ();}catch (IOException e){
e.printStackTrace ();
}
}
}
2012-03-06更新:
有网友提出调用in.close()是否会关闭底层socket,事情是这样的:[plain] view plain copy print?
回复kangkang203:感谢你提出的这个问题。
首 先我文中提出的方法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的关闭。
另外,由于http本身我们把它当做“短连接”,所以在一次请求交互完成后仍然打开socket的意义不是很大,毕竟它不像长连接那样在一个连接建立之后会有很多次数据交互。我们试用连接管理器的更多意义在于它对连接的管理。
好说完了连接池的使用流程,现在来说一说连接池在使用时最重要的几个参数。我用4.1的版本实现了一个简单的HttpConnectionManager,代码如下:
[java] view plain copy print?
public class HttpConnectionManager {
private static HttpParams httpParams;
private static ClientConnectionManager connectionManager;
/**
* 最大连接数
*/
public final static int MAX_TOTAL_CONNECTIONS = 800;
/**
* 获取连接的最大等待时间
*/
public final static int WAIT_TIMEOUT = 60000;
/**
* 每个路由最大连接数
*/
public final static int MAX_ROUTE_CONNECTIONS = 400;
/**
* 连接超时时间
*/
public final static int CONNECT_TIMEOUT = 10000;
/**
* 读取超时时间
*/
public final static int READ_TIMEOUT = 10000;
static {
httpParams = new BasicHttpParams();
// 设置最大连接数
ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);
// 设置获取连接的最大等待时间
ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);
// 设置每个路由最大连接数
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);
// 设置连接超时时间
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
// 设置读取超时时间
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
connectionManager = new ThreadSafeClientConnManager(httpParams, registry);
}
public static HttpClient getHttpClient() {
return new DefaultHttpClient(connectionManager, httpParams);
}
}
最大连接数、获取连接的最大等待时间、读取超时时间 这些配置应该比较容易理解,一般的连接池都会有这些配置,比较特别的是 每个路由(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,没想到一个小版本升级会有这么大变化。
官网教程举例了新的连接池设置:
[java] view plain copy print?
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(
new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
// Increase max total connection to 200
cm.setMaxTotalConnections(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.setMaxForRoute(new HttpRoute(localhost), 50);
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/tutorial/html/connmgmt.html#d4e638
公司代码:
public class HttpClientMgr {//HttpConnectionManager连接池 private static ClientConnectionManager cm; private static HttpParams params; public static final void init() { params = new BasicHttpParams(); // // 设置最大连接数 ,增加最大连接到200 ConnManagerParams.setMaxTotalConnections(params, 200); // 设置每个路由最大连接数 ,增加每个路由的默认最大连接到20 ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20); ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory .getSocketFactory(), 80)); cm = new ThreadSafeClientConnManager(params, schemeRegistry); } public static final DefaultHttpClient getHttpClient() { DefaultHttpClient httpClient = new DefaultHttpClient(cm, params); httpClient.getCookieSpecs().register("kds", new CookieSpecFactory() { @Override public CookieSpec newInstance(HttpParams params) { // TODO Auto-generated method stub return new KdsCookieSpec(); } }); //httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, // "kds"); HttpClientParams.setCookiePolicy(httpClient.getParams(), "kds"); return httpClient; } public static final void shutdown() { cm.shutdown(); } public static final String[] DATE_PATTERNS = new String[] { "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", "EEE MMM d HH:mm:ss yyyy", "EEE, dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MMM-yyyy HH-mm-ss z", "EEE, dd MMM yy HH:mm:ss z", "EEE dd-MMM-yyyy HH:mm:ss z", "EEE dd MMM yyyy HH:mm:ss z", "EEE dd-MMM-yyyy HH-mm-ss z", "EEE dd-MMM-yy HH:mm:ss z", "EEE dd MMM yy HH:mm:ss z", "EEE,dd-MMM-yy HH:mm:ss z", "EEE,dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MM-yyyy HH:mm:ss z", "E, dd-MMM-yyyy HH:mm:ss zzz", "EEEE, dd-MMM-yy HH:mm:ss zzz" }; static class KdsCookieSpec extends BrowserCompatSpec { @SuppressWarnings("deprecation") public KdsCookieSpec() { super(); registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(DATE_PATTERNS) { @Override public void parse(SetCookie cookie, String value) throws MalformedCookieException { super.parse(cookie, value); //Logger.d("tag", "KdsCookieSpec value:"+value); } }); } } }
相关文章推荐
- HttpURLConnection用法详解
- Socket、TCP/IP、HTTP、FTP和网络编程
- 异步网络请求
- Okhttp
- Python实现人工神经网络(反向传播算法)
- 使用socket套接字通过http协议实现文档下载功能
- ssl和https协议详解
- Windows 下 Apache HTTP Server 安装、配置以及与 Tomcat 的整合(附图)
- 真实的网络赚钱经历:另类推广引流操作CPA!
- MFC之HTTP post请求
- MFC Http Post请求步骤
- HTTP协议详解(真的很经典)
- Mac本地搭建Apache服务器步骤,方便我们开发中网络测试
- OkHttp用法
- Vmware中OpenWrt15.05的网络连接设置
- 浅谈TCP协议
- 网络中节点的通信方式术语
- 文章5:http协议中content-length 以及chunked编码分析
- RTP/RTCP/RTSP/SIP/SDP 关系
- 网络性能测试和标准