小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

HttpClient容易忽視的細節(jié)

 關平藏書 2019-05-13

Apache commons 系列的HttpClient 相信大家都用過,選擇它而非JDK 的java.net.HttpURLConnection ,是為了使用HttpClient 封裝的幾個實用的功能。

目前使用最多的版本還是httpclient-3.x ,在官網(wǎng)http://hc./httpclient-3.x/tutorial.html 有這么一段示例代碼:

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.HttpMethodParams;

import java.io.*;

public class HttpClientTutorial {
  
  private static String url = "http://www./";

  public static void main(String[] args) {
    // Create an instance of HttpClient.
    HttpClient client = new HttpClient();

    // Create a method instance.
    GetMethod method = new GetMethod(url);
    
    // Provide custom retry handler is necessary
    method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
    		new DefaultHttpMethodRetryHandler(3, false));

    try {
      // Execute the method.
      int statusCode = client.executeMethod(method);

      if (statusCode != HttpStatus.SC_OK) {
        System.err.println("Method failed: " + method.getStatusLine());
      }

      // Read the response body.
      byte[] responseBody = method.getResponseBody();

      // Deal with the response.
      // Use caution: ensure correct character encoding and is not binary data
      System.out.println(new String(responseBody));

    } catch (HttpException e) {
      System.err.println("Fatal protocol violation: " + e.getMessage());
      e.printStackTrace();
    } catch (IOException e) {
      System.err.println("Fatal transport error: " + e.getMessage());
      e.printStackTrace();
    } finally {
      // Release the connection.
      method.releaseConnection();
    }  
  }
}

大部分人也是以這個為規(guī)范來使用的,但是注意有一段關于“Release the Connection”的說明:
This is a crucial step to keep things flowing. 
We must tell HttpClient that we are done with 
the connection and that it can now be reused. 
Without doing this HttpClient will wait 
indefinitely for a connection to free up so 
that it can be reused.
我看得也不是很明白,意思是我們必須在使用后調(diào)用
method.releaseConnection();
來告訴HttpClient 這個連接可以重用了。

這個在串行的處理中或許很有用,但是我所遇到的情況是多線程并發(fā)下,不能共享同一個HttpClient 實例,按照官方示例寫好代碼后,程序跑起來似乎沒什么問題,但是隨著時間的累計,有一天突然發(fā)現(xiàn)這個模塊不工作了,查看了一下當前的網(wǎng)絡連接,這個java 程序同一個地址保持著200多個CLOSE_WAIT 的連接,好吧,連接沒有釋放。

為什么沒有釋放?查看doc,有這樣的說明:

Releases the connection being used by this HTTP method. 
In particular the connection is used to read the response
(if there is one) and will be held until the response has 
been read. If the connection can be reused by other HTTP 
methods it is NOT closed at this point.

注意最后一句,如果該連接可以重用則不關閉,是“可以重用”,當然可以重用了,就在那兒等著我去重用,可是我都是新建的實例,怎么重用

查看源碼,找到HttpClient 的構(gòu)造方法,有一個可以指定HttpConnectionManager ,然后這個HttpConnectionManager 又有一個實現(xiàn)的構(gòu)造:

public SimpleHttpConnectionManager(boolean alwaysClose)
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 false. 
Otherwise the connection manager will always close 
connections upon release.
Parameters:
alwaysClose - if set true, the connection manager will 
always close connections upon release.

顯然alawaysClose 的默認值是false ,在釋放后連接并不總是會關閉。

所以,必須

HttpClient client = new HttpClient(new HttpClientParams(),
new SimpleHttpConnectionManager(true));
Java代碼  收藏代碼
  1. HttpClient client = new HttpClient();  
  2. HttpMethod method = new GetMethod("http://www.");  
  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是發(fā)現(xiàn)一次循環(huán)發(fā)送大量請求到服務器會導致APACHE服務器的鏈接被占滿,后續(xù)的請求便排隊等待。
我服務器端APACHE的配置
Java代碼  收藏代碼
  1. Timeout 30  
  2. KeepAlive On   #表示服務器端不會主動關閉鏈接  
  3. MaxKeepAliveRequests 100  
  4. KeepAliveTimeout 180   

因此這樣的配置就會導致每個鏈接至少要過180S才會被釋放,這樣在大量請求訪問時就必然會造成鏈接被占滿,請求等待的情況。
在通過DEBUH后發(fā)現(xiàn)HttpClient在method.releaseConnection()后并沒有把鏈接關閉,這個方法只是將鏈接返回給connection manager。如果使用HttpClient client = new HttpClient()實例化一個HttpClient connection manager默認實現(xiàn)是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有個構(gòu)造函數(shù)如下
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. }  

看方法注釋我們就可以看到如果alwaysClose設為true在鏈接釋放之后connection manager 就會關閉鏈。在我們HttpClient client = new HttpClient()這樣實例化一個client時connection manager是這樣被實例化的
Java代碼  收藏代碼
  1. 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();
在method.releaseConnection();之后加上
Java代碼  收藏代碼
  1. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

shutdown源代碼很簡單,看了一目了然
Java代碼  收藏代碼
  1. public void shutdown() {  
  2.     httpConnection.close();  
  3. }  

方法三:
實例化代碼使用: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. }  

將idleTimeout設為0可以確保鏈接被關閉。
以上這三種方法都是有客戶端主動關閉TCP鏈接的方法。下面再介紹由服務器端自動關閉鏈接的方法。
方法四:
代碼實現(xiàn)很簡單,所有代碼就和最上面的事例代碼一樣。只需要在HttpMethod method = new GetMethod("http://www.");加上一行HTTP頭的設置即可
Java代碼  收藏代碼
  1. method.setRequestHeader("Connection", "close");  

看一下HTTP協(xié)議中關于這個屬性的定義:
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
現(xiàn)在再說一下客戶端關閉鏈接和服務器端關閉鏈接的區(qū)別。如果采用客戶端關閉鏈接的方法,在客戶端的機器上使用netstat –an命令會看到很多TIME_WAIT的TCP鏈接。如果服務器端主動關閉鏈接這中情況就出現(xiàn)在服務器端。
參考WIKI上的說明http://wiki./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的狀態(tài)會出現(xiàn)在主動關閉鏈接的這一端。TCP協(xié)議中TIME_WAIT狀態(tài)主要是為了保證數(shù)據(jù)的完整傳輸。具體可以參考此文檔:
http://www.softlab./facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7
另外強調(diào)一下使用上面這些方法關閉鏈接是在我們的應用中明確知道不需要重用鏈接時可以主動關閉鏈接來釋放資源。如果你的應用是需要重用鏈接的話就沒必要這么做,使用原有的鏈接還可以提供性能。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多