`

httpclient的并发连接问题

    博客分类:
  • java
阅读更多

昨天的搜索系统又出状况了,几个库同时重建索引变得死慢。经过一个上午的复现分析,确定问题出现httpclient的使用上(我使用的是3.1这个被广 泛使用的遗留版本)。搜索系统在重建索引时,是并发多个线程(默认是8个)不停的从PHP客户端取数据(当然,从另一个角度来说,搜索系统是客户 端,PHP端是服务端),取回后放到一个队列里由单独的一个或多个线程更新索引。在测试环境复现发现,对于一个请求,PHP端打印耗时是1-2秒,但搜索 端打印在4-6秒。这种耗时差别也就两种可能性,一个是PHP端返回到搜索端接受完耗时太长,另一个就是搜索端在真正发给PHP端数据前等待了很久。因为 有了之前的jetty7的困顿,起初我怀疑是传输数据的问题。因为请求数据的代码部分我只是简单的使用了httpclient,所以只能从 httpclient着手分析。我想到把PHP端和搜索端的请求起始和结束时间都打出来对照一下,不过在这样做之前我把搜索端的并发请求线程数调到了1, 看下单线程情况下效果如何,结果惊奇地发现PHP端和搜索端的耗时相近。所以,可以确定,是httpclient的并发连接处理上可能存在问题。不消说, 翻开httpclient API中和配置相关的接口,结果找到HttpConnectionManagerParams类中下面两个函数:

	public

 void

 setDefaultMaxConnectionsPerHost(

int

 maxHostConnections)

;


	public

 void

 setMaxTotalConnections(

int

 maxTotalConnections)

;

httpclient在处理请求连接方面使用了连接池,它内部实际上有两种连接池,一种是全局的ConnectionPool,一种是每主机(per- host)HostConnectionPool。参数maxHostConnections就HostConnectionPool中表示每主机可保持 连接的连接数,maxTotalConnections是ConnectionPool中可最多保持的连接数。每主机的配置类是 HostConfiguration,HttpClient有个int executeMethod(final HostConfiguration hostConfiguration, final HttpMethod method)方法可以指定使用哪个HostConfiguration,不过多数情况都是不显示指定HostConfiguration,这样 httpclient就用了默认的HostConfiguration=null,也就是说所有的请求可以认为指自同一个主机。如果不设置这两个参 数,httpclient自然会用默认的配置,也就是MultiThreadedHttpConnectionManager中的:

    /** The default maximum number of connections allowed per host */


    public

 static

 final

 int

 DEFAULT_MAX_HOST_CONNECTIONS =

 2

;

   // Per RFC 2616 sec 8.1.4


 
    /** The default maximum number of connections allowed overall */


    public

 static

 final

 int

 DEFAULT_MAX_TOTAL_CONNECTIONS =

 20

;

默认的maxHostConnections大小只有2,也就是说,在我并发8个线程请求数据时,实际上会有6个线程处于等待被调度,这也就解释上面的现 象了。再看看上面的注释,我从http://www.faqs.org/rfcs/rfc2616.html找到从了RFC 2616 sec 8.1.4 Practical Considerations段落的最后一段:

Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion.

看这叙述,也就表明人家httpclient设置maxHostConnections为2是有根有据的。不过,这种设置显然适合的是浏览器这种客户端, 但我相信,多数使用httpclient并不希望有这种默认的限制。而它默认的只有20的maxTotalConnections也太有些吝啬了。我后来 浏览solr的客户端server类CommonsHttpSolrServer的代码发现了下面的段落,solr要比我更了解httpclient:

    _httpClient =

 (

client ==

 null

)

 ?

 new

 HttpClient(

new

 MultiThreadedHttpConnectionManager(

)

)

 :

 client;


    if

 (

client ==

 null

)

 {


      // set some better defaults if we created a new connection manager and client


      // increase the default connections


      this

.setDefaultMaxConnectionsPerHost

(

 32

 )

;

  // 2


      this

.setMaxTotalConnections

(

 128

 )

;

 // 20


    }

对于httpclient,特别指出的是它的MultiThreadedHttpConnectionManager,看名字好像是多线程并发请求似的, 其实不是,但它也确实用到了多线程,那是在发现连接不够用时起个等待线程wait信号,这个名称的含义应该是多线程情况线程安全的 HttpConnectionManager。
使用httpclient还有两点经验,一个是创建的MultiThreadedHttpConnectionManager 实例最好是全局的,否则会有多个连接池,而HttpClient是线程安全的,可以多个实例。另一个是,在处理请求的最后,也就是finnaly里中,要 调用method.releaseConnection();回收连接,否则连接池就可能会爆了。

补充:写完之后倒在床上,我又想起了几个问题,这里补充上:
1、系统原先重建索引隐约记得速度还是可以的,为什么现在变慢得如此明显?这有两方面的原因,一个是原来系统取数据是用的单线程(我后来发现单取数据取数 据速度跟不上更新索引的速度所以改成多线程),另一个是,当时重建没有一下子同时开启数个库。所以,即便是同样的代码,环境变了,效果也可能变了。当这种 改变悄然发生了,程序员却没有捕捉到,才会第一直觉感到问题的诡异。
2、对于长时间不能获得连接的情况,httpclient是否有warn日志报出来?因为我使用了httpclient的 getResponseBodyAsStream方法,而它会打出warn日志,所以我是关掉了httpclient的warn级别的。所以,我又检查了 httpclient的代码,可惜没看到相关warning log,这点httpclient可以改进下。不过httpclient现在都是4时代了,而我使用的还是3.1的,而3.x已经被停止更新了,所以再采 用httpclient可以考虑4版本的,尽管现在能见到的代码几乎都是用的3.x系列的。
3、httpclient的文档是否有特别提到连接数配置的情况?我翻看了一下,确实在关于threading一页中有提到。不过,我等使用它时显然没有完整阅毕它的文档。

 

多线程编程

HttpClient 中使用多线程的一个主要原因是可以一次执行多个方法。在执行期间,每一个方法都使用一个 HttpConnection 实例。 由 于在同一时间多个连接只能安全地用于单一线程和方法和有限的资源,我们就必须确保连接分配给正确的方法。而 MultiThreadedHttpConnectionManager 完全可以代替我们完成这一项工作,这样我们就不必去考虑多线程带来安全的问题。

MultiThreadedHttpConnectionManager connectionManager =

                 new MultiThreadedHttpConnectionManager();

          HttpClient client = new HttpClient(connectionManager);

以上代码中的 HttpClient 就在多线程中执行多个方 法了。当我们再次调用 httpClient.executeMethod() 方法时,就会去 Connection Manager 中去请求 HttpConneciton 的实例,这样就避免了线程安全问题,因为 HttpClient 已经帮我们做了。

 

Options

 

MultThreadedHttpConnectionManager 参数配置:

 

connectionStaleCheckingEnabled :这个标志对所有已经创建的 connections 都适用。除特殊情况外,此值应该设置成 true

maxConnectionsPerHost :最大连 接数,默认是 2

maxTotalConnections :最大活动连 接数,默认是 20

 

释放连接

 

connection management 比较重要 的是当连接不再使用时,一定要手动释放。这样做的原因是 HttpClient 不能够确定哪个方法不被使用,哪个方法还在使用。这是因为 Response body 不是由 HttpClient 来自动读取其数据的,而是由使用 HttpClient 的应用程序来完成的。当读取 Response 的数据是时,必须使用此方法的连接。这样,在 Response 的数据在读取前, HttpClient 是没有释放连接的。所有这就要求 在读取完 Response 的数据后,应用程序及时的使用 releaseConnection ()方法来释放连接。

 

MultiThreadedHttpConnectionManager connectionManager =

                 new MultiThreadedHttpConnectionManager();

          HttpClient client = new HttpClient(connectionManager);

                     ...

        // and then from inside some thread executing a method

        GetMethod get = new GetMethod(“http://httpcomponents.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

            // manager

            get.releaseConnection();

        }

 

特别注意, 无论执行的方法或是否也不例外被抛出。对于每一个 HttpClient.executeMethod 方法必须有一个 method.releaseConnection )来释放连接。

分享到:
评论
1 楼 burningblood 2015-02-10  
最近也遇到了这个细节问题。我用的是4,里面没有 get.releaseConnection()。
realseConnection在新版中作为ConnManager.realseConnection(con...)的方法了。这个释放操作在execute之后自动执行了,自己不需要去释放。之后当不需要再使用httpclient时,调用shutdown关闭整个连接池。

相关推荐

    使用java的HttpClient实现多线程并发

    主要介绍了使用java的HttpClient实现多线程并发的相关资料,需要的朋友可以参考下

    HttpClient接口调用工具类 (附带调用demo)

    HttpClient接口调用工具类 (Post请求 get请求 put请求 delete请求 (附带调用demo) 下载就可以用 ) 可设置httpclient的连接池大小,连接池最大并发连接数,单路由最大并发数设

    HttpAsyncClient 4.1 异步HttpCLient

    HttpAsyncClient基于事件驱动的编程模型,...HttpAsyncClient 并不是要替换 Apache 的 HttpClient ,二者是互补关系,在一些需要进行大量并发连接的客户端以及对性能要求比较高的场景下,HttpAsyncClient 更加适合。

    Http连接池工具类

    两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包...一般情况下,普通使用HttpClient已经能满足我们的需求,不过有时候,在我们需要高并发大量的请求网络的时候,还是用“连接池”这样的概念能提升吞吐量。

    httpclient-helper:一个包含Spring的HttpClient的项目,该项目的成本,您可以高效,轻松地执行doGet和doPost

    HttpClient的一个帮助,对外只暴露一个多例的HttpClientApiService,该类封装了doGet和doPost等常用的Http请求方式使用了HttpClient的连接池技术,降低了延迟 支持了更大的并发,大大降低了繁琐的封装步骤 ...

    05丨HTTP调用:你考虑到超时、重试、并发了吗?

    最后,需要考虑框架是否会像浏览器那样限制并发连接数,以免在服务并发很大的情况下,HTTP 调用的并发数限制成为瓶颈。Spring Cloud 是 Java 微服务架构的代表性框架。如果使用 Spring Cloud 进行微服务开发,就会...

    开涛高可用高并发-亿级流量核心技术

    4.2.1 限流总并发/连接/请求数 69 4.2.2 限流总资源数 70 4.2.3 限流某个接口的总并发/请求数 70 4.2.4 限流某个接口的时间窗请求数 70 4.2.5 平滑限流某个接口的请求数 71 4.3 分布式限流 75 4.3.1 Redis+Lua实现 ...

    httpclient-x:httpclient 框架扩展

    KeepAliveHttpClientsTest - 基于"Keep-Alive HTTP并发连接有效及失效过期而被关闭"的测试策略2014.11.03[NEW] CustomUserTokenHandler - 自定义的用户标记处理器(UserTokenHandler)实现2014.11.01[NEW] ...

    .net core并发请求发送HttpWebRequest的坑解决

    但是在.net core中却无效,因为core不使用 ServicePointManager 管理连接数,在core中只有使用HttpClient,HttpCilentFactory来管理连接数,如果在core中使用 ServicePointManager 不但不起作用,并且大量并发使用...

    android-async-http-1.4.11.zip

    主要特征如下: 处理异步Http请求,并通过匿名内部类处理回调结果 Http请求均位于非UI线程,不会阻塞UI操作 通过线程池处理并发请求 处理文件上传、下载 响应结果自动打包JSON格式 自动处理连接断开时请求重连

    http-client:基于Netty的Java高性能面向吞吐量的HTTP客户端库

    在许多小的优化中,只要有可能就重用连接,这通过减少连接建立开销来大大减少总请求执行时间。1.1 版快要准备好了请务必检查。 除了一些类重命名之外,面向用户的 API 几乎保持不变——过渡应该是平滑的。依赖关系...

    Android-async-http 源码及jar包

    是基于Apache HttpClient库的。可以方便快速高效的进行网络数据请求 和发送,文件下载和上传。 特点: 清晰的网络请求回调 请求使用ThreadPool,限制并发资源使用情况 GET / POST基于参数构 建使用(RequestParams)...

    java开源包4

    HttpAsyncClient 的出现并不是为了替换 HttpClient,而是作为一个补充用于需要大量并发连接,对性能要求非常高的基于HTTP的原生数据通信,而且提供了事件驱动的 API。 NIO网络框架 xSocket xSocket是一个轻量级的...

    Java源码 SpringMVC Mybatis Shiro Bootstrap Rest Webservice

    项目Maven构建,真实大型互联网架构,做到高并发,大数据处理,整个项目使用定制化服务思想,提供模块化、服务化、原子化的方案,将功能模块进行拆分,可以公用到所有的项目中。架构采用分布式部署架构,所有模块...

    基于SpringBoot+Quartz的轻量级分布式定时任务调度系统源码+项目说明+sql数据库.zip

    - 是否并发执行:单机串行或者并发执行 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通。 2、项目适用人群:计科、信息安全、数据科学与大数据技术、...

    YurunHttp:YurunHttp 是开源的 PHP HTTP 客户端,支持链式操作,简单易用。完美支持Curl、Swoole 协程。QQ群:17916227

    Cookies 管理上传及下载请求头和响应头失败重试自动重定向HTTP 代理方式请求SSL 证书(HTTPS)并发批量请求HTTP2WebSocketCurl & Swoole 环境智能兼容连接池开发手册文档:API 文档:欢迎各位加入技术支持群17916227...

    android好用的框架(封装了数据库,注解,网络,图片缓存的框架)

    1、更新FinalBitmap模块,解决线程并发没有回收线程的问题 2、重写了FinalHttp模块 具体 change log 如下: FinalBitmap添加三个方法 public void onResume() public void onPause() public void onDestroy() ...

    JAVA上百实例源码以及开源项目

    1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高...

    JAVA上百实例源码以及开源项目源代码

    1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高...

Global site tag (gtag.js) - Google Analytics