最近在处理百度知道接口的问题时,遇到了下面的问题。在本机测试一直没有问题,因为测试的时间太短。而放到服务器上面就出现了下面的异常。而且是一直的出现。google 一下,原来是http连接出现了异常没有被关闭导致。写下来备查!
java.net.SocketException: Too many open files at java.net.Socket.createImpl(Socket.java:397) at java.net.Socket.<init>(Socket.java:371) at java.net.Socket.<init>(Socket.java:249) at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80) at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122) at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707) at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387) at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323) at com.thhc.mylegist.baidu.BaiDuHttpFactory.getQuestionList(BaiDuHttpFactory.java:284) at com.thhc.mylegist.baidu.BaiDuTask.run(BaiDuTask.java:21) at java.util.TimerThread.mainLoop(Timer.java:512) at java.util.TimerThread.run(Timer.java:462)
下面是转载的内容:
Java代码 1.HttpClient client = new HttpClient(); 2.HttpMethod method = new GetMethod("http://www.apache.org"); 3.try { 4. client.executeMethod(method); 5. byte[] responseBody = null; 6. 7. responseBody = method.getResponseBody(); 8. 9.} catch (HttpException e) { 10. // TODO Auto-generated catch block 11. e.printStackTrace(); 12.} catch (IOException e) { 13. // TODO Auto-generated catch block 14. e.printStackTrace(); 15.}finally{ 16. method.releaseConnection(); 17. 18.} HttpClient client = new HttpClient(); HttpMethod method = new GetMethod("http://www.apache.org"); try { client.executeMethod(method); byte[] responseBody = null; responseBody = method.getResponseBody(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ method.releaseConnection(); } 大部分人使用HttpClient都是使用类似上面的事例代码,包括Apache官方的例子也是如此。最近我在使用HttpClient是发现一次循环发送大量请求到服务器会导致APACHE服务器的链接被占满,后续的请求便排队等待。 我服务器端APACHE的配置 Java代码 1.Timeout 30 2.KeepAlive On #表示服务器端不会主动关闭链接 3.MaxKeepAliveRequests 100 4.KeepAliveTimeout 180 Timeout 30 KeepAlive On #表示服务器端不会主动关闭链接 MaxKeepAliveRequests 100 KeepAliveTimeout 180 因此这样的配置就会导致每个链接至少要过180S才会被释放,这样在大量请求访问时就必然会造成链接被占满,请求等待的情况。 在通过DEBUH后发现HttpClient在method.releaseConnection()后并没有把链接关闭,这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下 Java代码 1./** 2. * The connection manager created with this constructor will try to keep the 3. * connection open (alive) between consecutive requests if the alwaysClose 4. * parameter is set to <tt>false</tt>. Otherwise the connection manager will 5. * always close connections upon release. 6. * 7. * @param alwaysClose if set <tt>true</tt>, the connection manager will always 8. * close connections upon release. 9. */ 10.public SimpleHttpConnectionManager(boolean alwaysClose) { 11. super(); 12. this.alwaysClose = alwaysClose; 13.} /** * The connection manager created with this constructor will try to keep the * connection open (alive) between consecutive requests if the alwaysClose * parameter is set to <tt>false</tt>. Otherwise the connection manager will * always close connections upon release. * * @param alwaysClose if set <tt>true</tt>, the connection manager will always * close connections upon release. */ public SimpleHttpConnectionManager(boolean alwaysClose) { super(); this.alwaysClose = alwaysClose; } 看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的 Java代码 1.this.httpConnectionManager = new SimpleHttpConnectionManager(); this.httpConnectionManager = new SimpleHttpConnectionManager(); 因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。 方法一: 把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。 Java代码 1.HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) ); HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) ); 方法二: 实例化代码使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 Java代码 1.((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown(); ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown(); shutdown源代码很简单,看了一目了然 Java代码 1.public void shutdown() { 2. httpConnection.close(); 3.} public void shutdown() { httpConnection.close(); } 方法三: 实例化代码使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下: Java代码 1.public void closeIdleConnections(long idleTimeout) { 2. long maxIdleTime = System.currentTimeMillis() - idleTimeout; 3. if (idleStartTime <= maxIdleTime) { 4. httpConnection.close(); 5. } 6.} public void closeIdleConnections(long idleTimeout) { long maxIdleTime = System.currentTimeMillis() - idleTimeout; if (idleStartTime <= maxIdleTime) { httpConnection.close(); } } 将idleTimeout设为0可以确保链接被关闭。 以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。 方法四: 代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可 Java代码 1.method.setRequestHeader("Connection", "close"); method.setRequestHeader("Connection", "close"); 看一下HTTP协议中关于这个属性的定义: HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, Connection: close 现在再说一下客户端关闭链接和服务器端关闭链接的区别。如果采用客户端关闭链接的方法,在客户端的机器上使用netstat –an命令会看到很多TIME_WAIT的TCP链接。如果服务器端主动关闭链接这中情况就出现在服务器端。 参考WIKI上的说明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes. TIME_WAIT的状态会出现在主动关闭链接的这一端。TCP协议中TIME_WAIT状态主要是为了保证数据的完整传输。具体可以参考此文档: http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7 另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。
评论:
8 楼 javatar 2008-11-23 引用 官方文档有建议使用空闲连接检查线程: Java代码 1.import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread; 2.// 创建线程 3.IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread(); 4.// 注册连接管理器 5.thread.addConnectionManager(httpClient.getHttpConnectionManager()); 6.// 启动线程 7.thread.start(); 8.// 在最后,关闭线程 9.thread.shutdown(); import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread; // 创建线程 IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread(); // 注册连接管理器 thread.addConnectionManager(httpClient.getHttpConnectionManager()); // 启动线程 thread.start(); // 在最后,关闭线程 thread.shutdown(); 7 楼 sdh5724 2008-10-29 引用 楼上的说法是对的, 但是, 性能来说, 是不合适的. 首先, 我想问你, 既然是轮询, 为什么HttpURLConnection要每次创建. 既然是个持续的过程, 就不应该每次HttpURLConnection对象重新创建, 你应该重复使用这个对象. HttpURLConnection会持有连接的, 如果断开, 我记得他会去尝试连接. 6 楼 SeanHe 2008-10-28 引用 kjah 写道 但使用netstat发现很多TIME_WAIT,时间长点后会出现端口都被占用的状况 address already in use :connect,使用HttpURLConnection.disconnect()也没有效果。 如果你是应为客户端出现很多的TIME_WAIT造成端口占用,你不妨试一下“方法四”在HTTP请求头上设置"Connection", "close"这样服务器会来主动关闭链接,这样就不会在客户端产生很多的TIME_WAIT 5 楼 kjah 2008-10-28 引用 我是google到这里的。。。 您对HttpClient 的关闭分析的很透彻 请教HttpURLConnection和URLConnection怎么关闭链接? 我需要轮询一个网址,但使用netstat发现很多TIME_WAIT,时间长点后会出现端口都被占用的状况 address already in use :connect,使用HttpURLConnection.disconnect()也没有效果。 4 楼 fly_ever 2008-09-11 引用 引用 另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。 正如你所说,实际上是只有并发量很高的时候才会发生链接占满的情况。 而如果没有这么高的并发量,我们没必要去实现代码来关闭连接。 因为我们在代码中实现主动关闭的功能的同时,也丧失了性能上的优势。 因此,我觉得遇到这种情况,应该把设置中的keepAliveTimeout调低,显得更好一些。 Java代码 1.1. Timeout 30 2.2. KeepAlive On #表示服务器端不会主动关闭链接 3.3. MaxKeepAliveRequests 100 4.4. KeepAliveTimeout 180 1. Timeout 30 2. KeepAlive On #表示服务器端不会主动关闭链接 3. MaxKeepAliveRequests 100 4. KeepAliveTimeout 180 不过,这里提供的HttpClient关闭的详细信息,还是很有价值的。 3 楼 jorsef 2008-09-10 引用 受用,非常感谢 2 楼 SeanHe 2008-09-06 引用 HttpClient client = new HttpClient(); 如果这样进行实例化,默认使用SimpleHttpConnectionManager作为connection manager,SimpleHttpConnectionManager没有连接池,只管理一个连接 1 楼 JavaTestJava 2008-09-05 引用 方法二: 实例化代码使用:HttpClient client = new HttpClient(); 在method.releaseConnection();之后加上 Java代码 ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown(); ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown(); shutdown源代码很简单,看了一目了然 Java代码 public void shutdown() { httpConnection.close(); } public void shutdown() { httpConnection.close(); } 方法二中的httpConnection.close();这里没有参数么? httpConnection为默认全局的那个连接?