HttpClient4.X 升级 入门 + http连接池使用

HttpClient4.X 升级 入门 + http连接池使用转载请注明出处,谢谢~http://blog.csdn.net/shootyou/archive/2011/05/12/6415248.aspx 在一次服务器异常的排查过程当中(服务器异常排查的过程我会另起文章),我们决定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。为什么使用HttpClient4?主要是HttpConnection

大家好,又见面了,我是你们的朋友全栈君。

转载请注明出处,谢谢~

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

 

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

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

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

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()来确认内容已经被消耗关闭连接)。具体方式如下:

...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,事情是这样的:

回复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,代码如下:

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,没想到一个小版本升级会有这么大变化。

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

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/httpclient/apidocs/org/apache/http/conn/params/ConnManagerParams.html

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/163244.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Python遗传和进化算法框架(一)Geatpy快速入门[通俗易懂]

    Python遗传和进化算法框架(一)Geatpy快速入门[通俗易懂]Geatpy是一个高性能实用型的Python遗传算法工具箱,提供一个面向对象的进化算法框架,经过全面改版后,新版Geatpy2目前由华南农业大学、暨南大学、华南理工等本硕博学生联合团队开发及维护。Website(includingdocumentation):http://www.geatpy.com Demo:https://github.com/geatpy-dev/geatp…

    2022年5月19日
    60
  • ADF检验「建议收藏」

    ADF检验「建议收藏」单位根检验是指检验序列中是否存在单位根,因为存在单位根就是非平稳时间序列了。单位根就是指单位根过程,可以证明,序列中存在单位根过程就不平稳,会使回归分析中存在伪回归。结果:(-0.04391111

    2022年8月1日
    7
  • 《JavaScript 模式》读书笔记(1)— 简介

    哇,看了自己最近的一篇文章,已经刚好一年来,其实那时候刚刚接触Jest,啥也不会(虽然现在其实也一样不会,嘿嘿),就想记录下工作中遇到的一些问题。但是后来在持续的工作中才发现,那两篇文章写的其实是很有

    2022年3月25日
    44
  • matlab 求矩阵秩,求矩阵秩的两种方法及MATLAB的应用

    matlab 求矩阵秩,求矩阵秩的两种方法及MATLAB的应用摘要: 高等代数是一门逻辑思维比较强和理论知识比较深的学科,它具有丰富的数学知识,涉及许多重要的数学思想,其在数学领域的应用很广泛,如行列式、矩阵的相关计算和求解线性方程组的解方面的应用等,求矩阵的秩运算是矩阵研究的一个重要内容,此外数学软件MATLAB在矩阵计算方面也提供了很多方法,本文主要介绍应用MATLAB求矩阵的秩运算的方法。关键词: 矩阵;秩;高等代数;MAT…

    2022年5月30日
    73
  • JS中promise是什么?

    JS中promise是什么?Promise是异步编程的一中解决方案,最早是由社区提出的,es6中正式的将其纳入,他是一个对象,可以获取到异步的操作,他相比传统的回调函数,更加的强大和合理,避免了回调地狱。所谓的Promise,简单的来说就是一个可以存放未来才能结束的任务或者事件。1.Promise实列有三个状态:-pending(进行中)-resolved(成功)-rejected(失败)当要处理某个任务的时候,promise的状态是pending,任务完成是状态就变成了resolved,任务失败状

    2022年4月30日
    96
  • Java和Python哪个更适合初学者的问题

    Java和Python哪个更适合初学者的问题我个人不是很喜欢讨论这个问题,为什么呢,每个人都学习能力不一样,你要是不行,哪个对于你也不简单。客观分析,这两种语言都在程序员的工具箱中都占有一席之地。学习哪个并不重要,个人觉得对于入门级同学来说,你要学的是“编程思想的基础知识“,而不是”特定的编程语言“。那这两门语言有区别吗?当然有,对于有一定编程经验的人来说…

    2022年7月7日
    20

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号