2025-02-17

 
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的配置 
 
Timeout 30   
KeepAlive On   #表示服務器端不會主動關閉鏈接   
MaxKeepAliveRequests 100   
KeepAliveTimeout 180   
因此這樣的配置就會導致每個鏈接至少要過180S才會被釋放,這樣在大量請求訪問時就必然會造成鏈接被占滿,請求等待的情況。 
在通過DEBUH後發現HttpClient在method.releaseConnection()後並沒有把鏈接關閉,這個方法隻是將鏈接返 回給connection manager。如果使用HttpClient client = new HttpClient()實例化一個HttpClient connection manager默認實現是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有個構 造函數如下 
 /**   
 * 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是這樣被實例化的 
 
1 this.httpConnectionManager = new SimpleHttpConnectionManager();  
 
因此alwaysClose默認是false,connection是不會被主動關閉的,因此我們就有瞭一個客戶端關閉鏈接的方法。 
 
方法一: 
 
把事例代碼中的第一行實例化代碼改為如下即可,在method.releaseConnection();之後connection manager會關閉connection 。 
 
1 HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  
 
方法二: 
 
實例化代碼使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之後加上 
 
1 ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  
 
shutdown源代碼很簡單,看瞭一目瞭然 
 public void shutdown() {    
  httpConnection.close();    
}  
 
方法三: 
 
實例化代碼使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之後加上 
client.getHttpConnectionManager().closeIdleConnections(0);此方法源碼代碼如下: 
 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頭的設置即可 
 
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 
 
另外強調一下使用上面這些方法關閉鏈接是在我們的應用中明確知道不需要重用鏈接時可以主動關閉鏈接來釋放資源。如果你的應用是需要重用鏈接的話就沒必要這麼做,使用原有的鏈接還可以提供性能。 

作者“ERDP技術架構”

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *