Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  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都是使用类似上面的事例代码,包括Apache官方的例子也是如此。最近我在使用HttpClient是发现一次循环发送大量请求到服务器会导致APACHE服务器的链接被占满,后续的请求便排队等待。
我服务器端APACHE的配置
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. Timeout 30  
  2. KeepAlive On   #表示服务器端不会主动关闭链接   
  3. MaxKeepAliveRequests 100  
  4. KeepAliveTimeout 180   

因此这样的配置就会导致每个链接至少要过180S才会被释放,这样在大量请求访问时就必然会造成链接被占满,请求等待的情况。
在通过DEBUH后发现HttpClient在method.releaseConnection()后并没有把链接关闭,这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  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. }  

看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. this.httpConnectionManager = new SimpleHttpConnectionManager();  

因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。
方法一:
把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  

方法二:
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

shutdown源代码很简单,看了一目了然
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. public void shutdown() {   
  2.     httpConnection.close();   
  3. }  

方法三:
实例化代码使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下:
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. public void closeIdleConnections(long idleTimeout) {   
  2.     long maxIdleTime = System.currentTimeMillis() - idleTimeout;   
  3.     if (idleStartTime <= maxIdleTime) {   
  4.         httpConnection.close();   
  5.     }   
  6. }  

将idleTimeout设为0可以确保链接被关闭。
以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。
方法四:
代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可
Java代码 【转】HttpClient关闭连接 【转】HttpClient关闭连接【转】HttpClient关闭连接
  1. method.setRequestHeader("Connection""close");  

 

根据TCP协议,主动发起关闭的一方,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime),缺省为240秒,在这个post中简洁的介绍了为什么需要这个状态。

值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。

HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个request/response,一个主要原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少,因为240秒的时间实在是忒长了点,对于Windows,修改注册表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。

 

相关文章: