HttpClient连接池抛出大量ConnectionPoolTimeoutException: Timeout waiting for connection异常排查

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

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

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


问题表现:

tomcat后台日志发现大量异常

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

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

linux运行:

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实现:

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);
	}

}


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

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

public static String readNet (String urlPath)
	{
		StringBuffer sb = new StringBuffer ();
		HttpClient client = null;
		InputStream in = null;
		InputStreamReader isr = null;
		try
		{
			client = HttpConnectionManager.getHttpClient();
			HttpGet get = new HttpGet();
            get.setURI(new URI(urlPath));
            HttpResponse response = client.execute(get);
            if (response.getStatusLine ().getStatusCode () != 200) {
                return null;
            }
            HttpEntity entity =response.getEntity();
            
            if( entity != null ){
            	in = entity.getContent();
            	.....
            }
            return sb.toString ();
			
		}
		catch (Exception e)
		{
			e.printStackTrace ();
			return null;
		}
		finally
		{
			if (isr != null){
				try
				{
					isr.close ();
				}
				catch (IOException e)
				{
					e.printStackTrace ();
				}
			}
			if (in != null){
				try
				{
					in.close ();
				}
				catch (IOException e)
				{
					e.printStackTrace ();
				}
			}
		}
	}

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

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

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

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

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

public static String readNet (String urlPath)
	{
		StringBuffer sb = new StringBuffer ();
		HttpClient client = null;
		InputStream in = null;
		InputStreamReader isr = null;
		HttpGet get = new HttpGet();
		try
		{
			client = HttpConnectionManager.getHttpClient();
            get.setURI(new URI(urlPath));
            HttpResponse response = client.execute(get);
            if (response.getStatusLine ().getStatusCode () != 200) {
            	get.abort();
                return null;
            }
            HttpEntity entity =response.getEntity();
            
            if( entity != null ){
            	in = entity.getContent();
            	......
            }
            return sb.toString ();
			
		}
		catch (Exception e)
		{
			get.abort();
			e.printStackTrace ();
			return null;
		}
		finally
		{
			if (isr != null){
				try
				{
					isr.close ();
				}
				catch (IOException e)
				{
					e.printStackTrace ();
				}
			}
			if (in != null){
				try
				{
					in.close ();
				}
				catch (IOException e)
				{
					e.printStackTrace ();
				}
			}
		}
	}

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


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

相关推荐
Statistical learning refers to a set of tools for modeling and understanding complex datasets. It is a recently developed area in statistics and blends with parallel developments in computer science and, in particular, machine learning. The field encompasses many methods such as the lasso and sparse regression, classification and regression trees, and boosting and support vector machines. With the explosion of “Big Data” problems, statistical learning has be- come a very hot field in many scientific areas as well as marketing, finance, and other business disciplines. People with statistical learning skills are in high demand. One of the first books in this area—The Elements of Statistical Learning (ESL) (Hastie, Tibshirani, and Friedman)—was published in 2001, with a second edition in 2009. ESL has become a popular text not only in statis- tics but also in related fields. One of the reasons for ESL’s popularity is its relatively accessible style. But ESL is intended for individuals with ad- vanced training in the mathematical sciences. An Introduction to Statistical Learning (ISL) arose from the perceived need for a broader and less tech- nical treatment of these topics. In this new book, we cover many of the same topics as ESL, but we concentrate more on the applications of the methods and less on the mathematical details. We have created labs illus- trating how to implement each of the statistical learning methods using the popular statistical software package R . These labs provide the reader with valuable hands-on experience.
版本号:7.0.1 开 发 者:同程网 发布时间:2014-12-19 文件大小:11.84MB 热度支持固件:2.1 及以上版本 分享到 同程旅游-特价景点门票.机票.酒店介绍 【同程旅游】国内手机景点订票NO.1,全国各地旅游景点全覆盖。周边自助游、出境游、邮轮、酒店、机票、团购快捷预订,航班动态、旅游攻略、火车汽车班次及时查询。 【软件功能】 1. 景点门票:[有票频道]免费获取景点门票!周周更新免费景点列表!快捷预订,订贵了还可申请赔付,赔付2倍差价,行程有变,随时可退,手机预订优惠更多,点评奖金1元提现; 2. 机票:提供接送机服务!再用不用排队等待!国内所有航线打折机票预订,快捷预订往返机票,航班动态实时查询,手机专享优惠,支持在线退款申请,并且实现微信支付; 3. 酒店:国内及港澳4万余家酒店预订,海量酒店房型照片以及真实住客的点评,入住发点评赢奖金,手机预订返得更多。 4. 周边游:200公里范围内美食、美景、住宿、购物、娱乐应有尽有; 5. 邮轮:皇家加勒比、歌诗达、丽星等全球知名邮轮公司邮轮预订,同程网邮轮独家包船预订; 6. 出境游:全球热门目的地旅游团预订、半自助游预订;跟团自助随便怎么玩。 7. 团购:全国及港澳特价酒店限时抢购,; 8. 扫一扫:扫描二维码,特价产品直接订; 9. 旅游攻略:国内外热门旅游目的地精品攻略、游记,同程网资深旅游编辑与近百位知名游记作家和千万'驴友'倾力打造,旅途感悟、目的地玩法、省钱妙招、消费陷阱提示尽在掌握之中; 10. 火车:支持在线退票和微信支付等多种支付方式。全国列车时刻、余票实时查询,还可查询中转汽车票信息;还有接火车服务!再也不用排队等待! 11. 语音搜索:想找什么,无需打字,只要说一声就ok啦。 12. 微社区:微社区新玩法,现场直播间山来给你登场!出去玩,实时信息一手掌握;不出门,听听别人怎么玩; 【软件特性】 1、无需注册:无需繁琐的注册流程,及时满足您的预订需求; 2、安全支付:同程5重体系全面保障您的用卡安全,更有银联、支付宝、微信等多种支付方式提供; 3、极速预订:十秒内完成预订; 4、服务保障:'同程旅游',致力于为您提供一站式便携旅行服务 v7.0.1更新: 【更新内容】 1、全新换肤,陪你清新一夏; 2、火车票支持在线预订啦,让你永远告别排队; 3、国际机票查询预订一站式搞定,想去哪里就飞哪里; 4、周边游限时秒杀特惠,等你抢; 5、攻略全新改版,周边吃喝玩乐,教你玩转目的地; 6、1元景点,凭电子票快速入园; 7、景点支持银联付款,在线支付更便捷;
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页