httpClient容易忽略的细节-关闭连接

时间:2022-02-07 15:44:48
ava代码   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容易忽略的细节-关闭连接
  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容易忽略的细节-关闭连接
  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容易忽略的细节-关闭连接
  1. this.httpConnectionManager = new SimpleHttpConnectionManager();  

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

方法二:  
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 
Java代码   httpClient容易忽略的细节-关闭连接
  1. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

shutdown源代码很简单,看了一目了然 
Java代码   httpClient容易忽略的细节-关闭连接
  1. public void shutdown() {  
  2.     httpConnection.close();  
  3. }  

方法三:  
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下: 
Java代码   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容易忽略的细节-关闭连接
  1. 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 

另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。


HTTP协议知识

为便于读者理解后文,先简述一些与HTTP性能密切相关的、又常常被工程师们所不深究的HTTP协议基础知识。

 

一,什么是HTTP KeepAliv 电子邮件

HTTP KeepAlive是就是通常所称的长连接。KeepAlive即服务器端为同一客户端保持连接一段时间(不立即关闭),以便于更多来自于此客户端的后续请求不断的利用此连接直至连接超时。

在HTTP1.0和HTTP1.1协议中都有对的KeepAlive的支持。其中HTTP1.0需要在请求头中增加“连接:保持活动”才能够支持,而HTTP1.1默认支持。

 

该属性的更多阐述:

1,下一个请求是在完成之前请求的响应被客户端接收的情况下才发出。因此需要在向客户端写完之前的请求的响应后才能触发。

2、HTTP协议是基于TCP协议的,故服务器端与客户端都有可能关闭连接。KeepAlive只是表明了服务器端面对连接的一种优化策略,而客户端也完全可以主动关闭之(不利用)。

 

二,KEEPALIVE的好处与坏处

KeepAlive带来的好处是可以减少HTTP连接的开销,提高性能。比如,同一页面中如有很多内嵌的图片、JS、CSS等请求,则可以利用此特新性,使用少量的连接数(IE下一般是2个)更快的下载下来,使得网页更快的展示出来。

 

QeepAlive的坏处是:

如果有大量不同的客户端同时(或瞬间)请求服务器端,且每一个客户端的都长期占用连接(比如:不关闭且ConnectionTimeOut设置过长)或服务器端也不快速失效连接(KeepAliveTimeout参数设置过大)的话,可能会快速占满服务器连接资源,导致更多的请求被排队或被拒绝或服务器down掉。

 

总结:浏览器作为一种HTTP客户端,充分的、很好的利用了HTTP协议的KeepAlive,让我们的浏览更加快速;而我们自写的HTTP客户端程序在KeepAlive特性(服务器已开启)下,需要以高数据量访问一个HTTP接口的时候,每一次请求应当尽快关闭连接释放资源(重点推荐)或者在同一连接上适当多发几次请求(不推荐)。