转自: http://blog.csdn.net/shootyou/article/details/6615051

今天解决了一个HttpClient的异常,汗啊,一个HttpClient使用稍有不慎都会是毁灭级别的啊。

这里有之前因为route配置不当导致服务器异常的一个处理:http://blog.csdn.net/shootyou/article/details/6415248

里面的HttpConnectionManager实现就是我在这里使用的实现。

问题表现:

tomcat后台日志发现大量异常

  1. org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection

时间一长tomcat就无法继续处理其他请求,从假死变成真死了。

linux运行:

  1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

发现CLOSE_WAIT的数量始终在400以上,一直没降过。

问题分析:

一开始我对我的HttpClient使用过程深信不疑,我不认为异常是来自这里。

所以我开始从TCP的连接状态入手,猜测可能导致异常的原因。以前经常遇到TIME_WAIT数过大导致的服务器异常,很容易解决,修改下sysctl就ok了。但是这次是CLOSE_WAIT,是完全不同的概念了。

关于TIME_WAIT和CLOSE_WAIT的区别和异常处理我会单独起一篇文章详细说说我的理解。

简单来说CLOSE_WAIT数目过大是由于被动关闭连接处理不当导致的。

我说一个场景,服务器A会去请求服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后自己并没有释放连接,那就会造成CLOSE_WAIT的状态了。

所以很明显,问题还是处在程序里头。

先看看我的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. }

看到没MAX_ROUTE_CONNECTIONS 正好是400,跟CLOSE_WAIT非常接近啊,难道是巧合?继续往下看。

然后看看调用它的代码是什么样的:

  1. public static String readNet (String urlPath)
  2. {
  3. StringBuffer sb = new StringBuffer ();
  4. HttpClient client = null;
  5. InputStream in = null;
  6. InputStreamReader isr = null;
  7. try
  8. {
  9. client = HttpConnectionManager.getHttpClient();
  10. HttpGet get = new HttpGet();
  11. get.setURI(new URI(urlPath));
  12. HttpResponse response = client.execute(get);
  13. if (response.getStatusLine ().getStatusCode () != 200) {
  14. return null;
  15. }
  16. HttpEntity entity =response.getEntity();
  17. if( entity != null ){
  18. in = entity.getContent();
  19. .....
  20. }
  21. return sb.toString ();
  22. }
  23. catch (Exception e)
  24. {
  25. e.printStackTrace ();
  26. return null;
  27. }
  28. finally
  29. {
  30. if (isr != null){
  31. try
  32. {
  33. isr.close ();
  34. }
  35. catch (IOException e)
  36. {
  37. e.printStackTrace ();
  38. }
  39. }
  40. if (in != null){
  41. try
  42. {
  43. <span style="color:#ff0000;">in.close ();</span>
  44. }
  45. catch (IOException e)
  46. {
  47. e.printStackTrace ();
  48. }
  49. }
  50. }
  51. }

很简单,就是个远程读取中文页面的方法。值得注意的是这一段代码是后来某某同学加上去的,看上去没啥问题,是用于非200状态的异常处理:

  1. if (response.getStatusLine ().getStatusCode () != 200) {
  2. return null;
  3. }

代码本身没有问题,但是问题是放错了位置。如果这么写的话就没问题:

  1. client = HttpConnectionManager.getHttpClient();
  2. HttpGet get = new HttpGet();
  3. get.setURI(new URI(urlPath));
  4. HttpResponse response = client.execute(get);
  5. HttpEntity entity =response.getEntity();
  6. if( entity != null ){
  7. in = entity.getContent();
  8. ..........
  9. }
  10. if (response.getStatusLine ().getStatusCode () != 200) {
  11. return null;
  12. }
  13. return sb.toString ();

看出毛病了吧。在这篇入门(HttpClient4.X 升级 入门 + http连接池使用)里头我提到了HttpClient4使用我们常用的InputStream.close()来确认连接关闭,前面那种写法InputStream in 根本就不会被赋值,意味着一旦出现非200的连接,这个连接将永远僵死在连接池里头,太恐怖了。。。所以我们看到CLOST_WAIT数目为400,因为对一个路由的连接已经完全被僵死连接占满了。。。

其实上面那段代码还有一个没处理好的地方,异常处理不够严谨,所以最后我把代码改成了这样:

  1. public static String readNet (String urlPath)
  2. {
  3. StringBuffer sb = new StringBuffer ();
  4. HttpClient client = null;
  5. InputStream in = null;
  6. InputStreamReader isr = null;
  7. HttpGet get = new HttpGet();
  8. try
  9. {
  10. client = HttpConnectionManager.getHttpClient();
  11. get.setURI(new URI(urlPath));
  12. HttpResponse response = client.execute(get);
  13. if (response.getStatusLine ().getStatusCode () != 200) {
  14. get.abort();
  15. return null;
  16. }
  17. HttpEntity entity =response.getEntity();
  18. if( entity != null ){
  19. in = entity.getContent();
  20. ......
  21. }
  22. return sb.toString ();
  23. }
  24. catch (Exception e)
  25. {
  26. get.abort();
  27. e.printStackTrace ();
  28. return null;
  29. }
  30. finally
  31. {
  32. if (isr != null){
  33. try
  34. {
  35. isr.close ();
  36. }
  37. catch (IOException e)
  38. {
  39. e.printStackTrace ();
  40. }
  41. }
  42. if (in != null){
  43. try
  44. {
  45. in.close ();
  46. }
  47. catch (IOException e)
  48. {
  49. e.printStackTrace ();
  50. }
  51. }
  52. }
  53. }

显示调用HttpGet的abort,这样就会直接中止这次连接,我们在遇到异常的时候应该显示调用,因为谁能保证异常是在InputStream in赋值之后才抛出的呢。

好了 ,分析完毕,明天准备总结下CLOSE_WAIT和TIME_WAIT的区别。

HttpClient连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查的更多相关文章

  1. HttpClient Timeout waiting for connection from pool 问题解决方案

    错误:org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool 前言 ...

  2. springboot使用RestTemplate+httpclient连接池发送http消息

    简介 RestTemplate是spring支持的一个请求http rest服务的模板对象,性质上有点像jdbcTemplate RestTemplate底层还是使用的httpclient(org.a ...

  3. Http持久连接与HttpClient连接池

    一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...

  4. Http 持久连接与 HttpClient 连接池

    一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连接,当交互完毕后会关闭这个连接. HTTP协议是全双工的协议, ...

  5. HttpClient 的Timeout waiting for connection from pool

    Timeout waiting for connection from pool 异常 httpClient大家用到地方会很多,先简单描述一下几个关键配置的意义 httpClient版本为4.5.1 ...

  6. HttpClient连接池的一些思考

    前言 使用apache的httpclient进行http的交互处理已经很长时间了,而httpclient实例则使用了http连接池,想必大家也没有关心过连接池的管理.事实上,通过分析httpclien ...

  7. HttpClient连接池

    HttpClient连接池,发现对于高并发的请求,效率提升很大.虽然知道是因为建立了长连接,导致请求效率提升,但是对于内部的原理还是不太清楚.后来在网上看到了HTTP协议的发展史,里面提到了一个属性C ...

  8. HttpClient实战三:Spring整合HttpClient连接池

    简介 在微服务架构或者REST API项目中,使用Spring管理Bean是很常见的,在项目中HttpClient使用的一种最常见方式就是:使用Spring容器XML配置方式代替Java编码方式进行H ...

  9. httpclient连接池在ES Restful API请求中的应用

    package com.wm.utils; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http ...

随机推荐

  1. structs2使用小结2

    回顾之前做过类似structs2的知识点总结 Structs2历史.处理流程及与1.0版本的区别 Structs2配置文件.拦截器.校验等 EL表达式在JS中使用 ${}El表达式不能直接在单独JS文 ...

  2. IE6 max-width max-height 不起作用 解决其兼容性问题

     .catelist dl dd ul li img {width: expression(this.width > 228 ? '228px': true); max-width:228px; ...

  3. 认识什么是SEO

    何为SEO? SEO是由英 文Search Engine Optimization缩写而来, 中文意译为“搜索引擎优化”,是指在了解搜索引擎自然排名机制的基础上,对网站进行内部及外部的调整优化,改进网 ...

  4. Java异步调用Future对象

    Future类存在于JDK的concurrent包中,主要用途是接收Java的异步线程计算返回的结果. 个人理解的使用场景大概如下: 有两个任务A和B,A任务中仅仅需要使用B任务计算成果,有两种方法实 ...

  5. flex渐变色制作圆角橙色按钮

    <?xml version="1.0" encoding="utf-8"?> <s:SparkButtonSkin xmlns:fx=&quo ...

  6. UGUI实现摇杆(模仿太极熊猫)

    核心代码: using UnityEngine; using System.Collections; using UnityEngine.UI; public delegate void Joysti ...

  7. iPhone/iPad全屏截图与区域截图的几种方法

    本文由社区会员taonavy分享 截取本区域(self.view): UIGraphicsBeginImageContext(CGSizeMake(self.view.frame.size.width ...

  8. 2. QT窗体间值的传递

    一.主窗体与子窗体传参 方法有很多,这里介绍一种通过重载子窗体的构造函数实现主窗体参数传入到子窗体,并通过QT信号和槽的机制实现子窗口到主窗口值的传递. 主和子窗体的设置如下: 主要实现功能为: 1 ...

  9. [HeadFirst-HTMLCSS学习笔记][第三章创建网页]

    一些基本元素 以下元素都可以用CSS变得更好看 q,<blockquote>,<em>,<br>, <strong>,ol ,ul,li,pre,cod ...

  10. 图片翻页效果引出的animate.css,很好玩,多动动吧~

    有一个项目,客户需要页面翻转的效果,需要应用在合作伙伴里面的图片上,一共有43张图片,我把它做成了随机定时的转动,鼠标经过时转动: animate.css科普文章:http://www.dowebok ...