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

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

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

问题表现:

tomcat后台日志发现大量异常

[plain] view plain copy print?
  1. org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection

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

linux运行:

[plain] view plain copy print?
  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实现:

[java] view plain copy print?
  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非常接近啊,难道是巧合?继续往下看。

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

[java] view plain copy print?
  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状态的异常处理:

[java] view plain copy print?
  1. if (response.getStatusLine ().getStatusCode () != 200) {
  2. return null;
  3. }

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

[java] view plain copy print?
  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,因为 对一个路由的连接已经完全被僵死连接占满了。。。

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

[java] view plain copy print?
  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连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查

    HttpClient连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查 参考文章: (1)HttpCli ...

  2. 关于druid数据连接池抛出的 connection holder is null 异常

    大概跑了三十分钟~~~这配置~~~这程序~~~ 然后华丽丽的报异常了~~~ 具体异常是这样的, ? 1 2 3 4 5 6 7 8 9 10 11 12 13 DEBUG: (BaseJdbcLogg ...

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

    以下文章来源方志朋的博客,回复"666"获面试宝典 一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp so ...

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

    转载自  Http 持久连接与 HttpClient 连接池 一.背景 HTTP协议是无状态的协议,即每一次请求都是互相独立的.因此它的最初实现是,每一个http请求都会打开一个tcp socket连 ...

  5. Axis2调用webservice报错 AxisFault: Timeout waiting for connection

    记录一次正式环境服务报错排查记录. 某日被通知线上服务告警,错误日志全是 Timeout waiting for connection 首先梳理项目架构,项目很简单,就是一个使用axis2构建的web ...

  6. 大并发下Timeout waiting for connection from pool 解决方案

    当前项目采用SpringCloud分布式架构 , 服务间采用REST接口请求 . 最近频发连接获取超时(本质上是HttpClient的连接超时)的问题 , 如下图 : 这是因为HttpClient在并 ...

  7. 【java】大并发下Timeout waiting for connection from pool 解决方案

    1.概述 今天我也遇到这个情况了. 转载:大并发下Timeout waiting for connection from pool 解决方案 但是我是FLink下遇到的,在processfuntion ...

  8. 问题备忘: httpclient连接池异常引发的惨案

    1. 问题描述 客户端A --> Ngnix --> 服务B Ngnix做服务B的负载,客户端访问服务B时,客户端偶尔会有抛出TimeoutException异常. 举个例子:如A在09: ...

  9. HttpClient连接池泄露

    现象 内部提供了一个文件上传的SDK,通过Apache HttpClient访问公司内部的文件服务器进行文件上传,然后偶尔会有同事反馈文件没有上传成功,但是文件服务器这边查看log也并没有收到请求, ...

最新文章

  1. 53个Python库,你必须要试试
  2. 版本管理工具 (git 或者 svn)
  3. dotNetCore操作Redis(含CentOS7哨兵模式部署)
  4. publiccms中,怎么修改默认的端口8080以及默认上下文名称
  5. JAX-RS 2.0的新功能– @BeanParam批注
  6. mysql union 与 union all 语法及用法
  7. linux查找指定修改时间的文件夹,linux 查找某个日期以后修改过哪些文件 shell脚本...
  8. Flutter开发系列教程
  9. 18-CSS问题-让多个div横排显示并设置间距解决方案
  10. creo工程图模板_Creo工程图的优越性总结(仅供参考),来自网友与君共享
  11. Sharepoint 浅谈 [转]
  12. 例2.7 输出梯形 - 九度教程第14题(排版题)
  13. JAVA算法:M个苹果放到N个盘子中的方法(JAVA代码)
  14. 华为手机字体改简体_华为繁体字体怎么更换 华为手机字体改简体
  15. Robotics: Computational Motion Planning(路径规划)笔记(完):人工势场法(Artificial Potential Fields)
  16. 微软小娜关闭服务器,图文详解彻底关闭windows10系统里的小娜(Cortana)语音服务-系统操作与应用 -亦是美网络...
  17. 2017年的知识清单
  18. 最小二乘法求线性回归方程
  19. 基于java客户关系管理系统
  20. 采用Windows PowerShell运行conda activate xx出现 C:\Users\xxx\Documents\WindowsPowerShell\profile.ps1报错问题

热门文章

  1. bfgs算法matlab程序,BFGS优化算法及应用实例.docx
  2. java自己实现读写锁_关于读写锁算法的Java实现及思考
  3. ckplayer php,ckplayer 网页视频播放插件
  4. 塔利亚菲科 ajax,[欧冠小组赛第4轮]阿贾克斯 3-1 中日德兰
  5. 智能车竞赛技术报告 | 节能信标组 - 宿迁学院 - MAkkaPakka
  6. 智能车竞赛技术报告 | 单车拉力组 - 长春理工大学- 追风零队 2021
  7. 2021年春季学期-信号与系统-第十五次作业参考答案-第三小题参考答案
  8. 用于计算无线充电中LCC补偿网络器件参数的小程序
  9. 2021年春季学期-信号与系统-第四次作业参考答案-MATLAB实验1
  10. HX711双通道AD转换模块