今天解决了一个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]}'
问题分析:
一开始我对我的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
- {
- <span style="color:#ff0000;">in.close ();</span>
- }
- 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 ();
其实上面那段代码还有一个没处理好的地方,异常处理不够严谨,所以最后我把代码改成了这样:
- 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的区别。
转自 http://www.blogjava.net/wangxinsh55/archive/2012/07/16/383210.html
相关推荐
### httpclient使用教程 #### HttpClient概述与重要性 在当今互联网时代,HTTP协议无疑是网络通信中最常用且至关重要的协议之一。随着技术的发展,越来越多的Java应用程序需要直接通过HTTP协议访问网络资源。尽管...
在提供的压缩包文件中,"第一个版本.rar"和"第二个版本.rar"可能包含了不同的HttpClient使用示例或者不同版本的代码。你可以解压并查看这些文件,以便更深入地理解和学习HttpClient的具体用法。每个版本可能包含不同...
《HttpClient使用详解》 HttpClient是Apache软件基金会的 HttpClient项目提供的一款强大的HTTP客户端工具,它允许开发者在Java应用程序中实现复杂的HTTP通信。这份8页的PDF文档深入解析了HttpClient的使用方法,...
### HttpClient 使用指南知识点详解 ...以上是基于提供的部分内容对HttpClient使用指南的相关知识点进行了详细说明。通过这些知识点的学习,可以更好地理解和掌握HttpClient的工作原理及其在实际开发中的应用。
在本示例中,我们将关注“httpclient使用post方法上传多个图片和其他参数的demo源码”,这是一个涉及到文件上传和参数传递的重要场景。 在Web开发中,POST方法常用于向服务器提交数据,比如表单数据或文件。...
这篇博客文章《HttpClient使用》(链接:https://leesonhomme.iteye.com/blog/491095)可能涵盖了HttpClient的基本用法和一些实用技巧。由于没有具体的描述,我们将基于HttpClient的一般知识点进行详细介绍。 1. **...
使用httpClient进行代理
Http协议使用封装jar包(commons-codec-1.3.jar、commons-httpclient-3.1.jar、commons-logging-1.1.jar) 简单使用方法: public static void main(String[] args) { // String str1 = "...;...
httpclient是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,本文档提供使用httpclient的使用方法
1. **导入依赖**:在Java项目中使用HttpClient,你需要先将所需的jar文件添加到类路径中。描述中的"所使用的jar.zip"可能包含了HttpClient的库文件,如httpclient、httpcore等。确保导入了这些库,才能使用...
1. **创建HttpClient对象**:这是发送请求的基础,通常使用`HttpClient`类的实例。 2. **创建请求方法**:根据需求创建`HttpGet`或`HttpPost`对象,分别对应GET和POST请求。 3. **设置请求参数**:可以通过`set...
2. **请求和响应模型**:HttpClient使用HttpRequest和HttpResponse对象封装HTTP请求和响应,便于处理请求头、请求体和响应头、响应体。 3. **身份验证和安全**:HttpClient支持多种身份验证机制,包括基本认证、...
在HttpClient的使用中,我们首先需要了解几个核心组件: 1. **HttpClient实例**:这是整个HTTP通信的基础,通过`HttpClientBuilder`创建,可以设置各种配置,如连接超时、重试策略等。 2. **HttpRequestBase**:这...