HttpClient4.X 升級 入門 + http連線池使用

xz43發表於2015-11-06

轉載地址:http://blog.csdn.net/shootyou/archive/2011/05/12/6415248.aspx

 

在一次伺服器異常的排查過程當中(伺服器異常排查的過程我會另起文章),我們決定使用HttpClient4.X替代HttpClient3.X或者HttpConnection。

為什麼使用HttpClient4?主要是HttpConnection沒有連線池的概念,多少次請求就會建立多少個IO,在訪問量巨大的情況下伺服器的IO可能會耗盡。

HttpClient3也有連線池的東西在裡頭,使用MultiThreadedHttpConnectionManager,大致過程如下:


  1. MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();  
  2. HttpClient client = new HttpClient(connectionManager);...// 在某個執行緒中。  
  3. GetMethod get = new GetMethod("");  
  4. try {  
  5. client.executeMethod(get);// print response to stdout  
  6. System.out.println(get.getResponseBodyAsStream());  
  7. finally {  
  8. // be sure the connection is released back to the connection   
  9. managerget.releaseConnection();  
  10. }  


可以看出來,它的方式與jdbc連線池的使用方式相近,我覺得比較不爽的就是需要手動呼叫releaseConnection去釋放連線。對每一個HttpClient.executeMethod須有一個method.releaseConnection()與之匹配。

 

HttpClient4在這點上做了改進,使用我們常用的InputStream.close()來確認連線關閉(4.1版本之前使用entity.consumeContent()來確認內容已經被消耗關閉連線)。具體方式如下:


  1. ...HttpClient client = null;InputStream in = null;  
  2. try{  
  3. client = HttpConnectionManager.getHttpClient();  
  4. HttpGet get = new HttpGet();  
  5. get.setURI(new URI(urlPath));  
  6. HttpResponse response = client.execute(get);  
  7. HttpEntity entity =response.getEntity();  
  8. if( entity != null ){   
  9.  in = entity.getContent();  
  10.  ....  
  11. }catch (Exception e){  
  12. ....  
  13. }finally{  
  14. if (in != null){  
  15. try{in.close ();}catch (IOException e){  
  16. e.printStackTrace ();  
  17. }  
  18. }  
  19. }  


2012-03-06更新:

有網友提出呼叫in.close()是否會關閉底層socket,事情是這樣的:


  1. 回覆kangkang203:感謝你提出的這個問題。  
  2. 首先我文中提出的方法in.close()它會觸發一個連線的釋放這個連線將重新被連線管理器收回,官網的原文是這麼說的:“Closing the input stream will trigger connection release...the underlying connection gets released back to the connection manager”。但是底層的socket是否會被關閉是不一定的,我看了部分原始碼(EofSensorInputStream)發現,大多數情況socket並不會關閉,而是否關閉socket貌似是由一個Watcher去決定的。所以in.close的呼叫不會引起socket的關閉。  
  3. 另外,由於http本身我們把它當做“短連線”,所以在一次請求互動完成後仍然開啟socket的意義不是很大,畢竟它不像長連線那樣在一個連線建立之後會有很多次資料互動。我們試用連線管理器的更多意義在於它對連線的管理。  

 


好說完了連線池的使用流程,現在來說一說連線池在使用時最重要的幾個引數。我用4.1的版本實現了一個簡單的HttpConnectionManager,程式碼如下:

  1. public class HttpConnectionManager {   
  2.   
  3.     private static HttpParams httpParams;  
  4.     private static ClientConnectionManager connectionManager;  
  5.   
  6.     /** 
  7.      * 最大連線數 
  8.      */  
  9.     public final static int MAX_TOTAL_CONNECTIONS = 800;  
  10.     /** 
  11.      * 獲取連線的最大等待時間 
  12.      */  
  13.     public final static int WAIT_TIMEOUT = 60000;  
  14.     /** 
  15.      * 每個路由最大連線數 
  16.      */  
  17.     public final static int MAX_ROUTE_CONNECTIONS = 400;  
  18.     /** 
  19.      * 連線超時時間 
  20.      */  
  21.     public final static int CONNECT_TIMEOUT = 10000;  
  22.     /** 
  23.      * 讀取超時時間 
  24.      */  
  25.     public final static int READ_TIMEOUT = 10000;  
  26.   
  27.     static {  
  28.         httpParams = new BasicHttpParams();  
  29.         // 設定最大連線數  
  30.         ConnManagerParams.setMaxTotalConnections(httpParams, MAX_TOTAL_CONNECTIONS);  
  31.         // 設定獲取連線的最大等待時間  
  32.         ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);  
  33.         // 設定每個路由最大連線數  
  34.         ConnPerRouteBean connPerRoute = new ConnPerRouteBean(MAX_ROUTE_CONNECTIONS);  
  35.         ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);  
  36.         // 設定連線超時時間  
  37.         HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);  
  38.         // 設定讀取超時時間  
  39.         HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);  
  40.   
  41.         SchemeRegistry registry = new SchemeRegistry();  
  42.         registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
  43.         registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));  
  44.   
  45.         connectionManager = new ThreadSafeClientConnManager(httpParams, registry);  
  46.     }  
  47.   
  48.     public static HttpClient getHttpClient() {  
  49.         return new DefaultHttpClient(connectionManager, httpParams);  
  50.     }  
  51.   
  52. }  


最大連線數、獲取連線的最大等待時間、讀取超時時間 這些配置應該比較容易理解,一般的連線池都會有這些配置,比較特別的是 每個路由(route)最大連線數 。

 

什麼是一個route?

 

這裡route的概念可以理解為 執行環境機器 到 目標機器的一條線路。舉例來說,我們使用HttpClient的實現來分別請求 的資源和 的資源那麼他就會產生兩個route。

 

這裡為什麼要特別提到route最大連線數這個引數呢,因為這個引數的預設值為2,如果不設定這個引數值預設情況下對於同一個目標機器的最大併發連線只有2個!這意味著如果你正在執行一個針對某一臺目標機器的抓取任務的時候,哪怕你設定連線池的最大連線數為200,但是實際上還是隻有2個連線在工作,其他剩餘的198個連線都在等待,都是為別的目標機器服務的。

 

怎麼樣蛋疼吧,我是已經有過血的教訓了,在切換到HttpClient4.1的起初沒有注意到這個配置,最後使得服務承受的壓力反而不如從前了,所以在這裡特別提醒大家注意。

 

HttpClient4.X 教程下載:


關於版本的補充:

網友w2449008821提醒之後我才發現在HttpClient4.1+的版本ConnManagerParams已經被Deprecated了。

我在寫這篇日誌的時候時候的httpclient 版本是4.0.3,從4.0版本之後ConnManagerParams被Deprecated,沒想到一個小版本升級會有這麼大變化。

官網教程舉例了新的連線池設定:

  1. SchemeRegistry schemeRegistry = new SchemeRegistry();  
  2. schemeRegistry.register(  
  3.          new Scheme("http"80, PlainSocketFactory.getSocketFactory()));  
  4. schemeRegistry.register(  
  5.          new Scheme("https"443, SSLSocketFactory.getSocketFactory()));  
  6.   
  7. ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);  
  8. // Increase max total connection to 200  
  9. cm.setMaxTotalConnections(200);  
  10. // Increase default max connection per route to 20  
  11. cm.setDefaultMaxPerRoute(20);  
  12. // Increase max connections for localhost:80 to 50  
  13. HttpHost localhost = new HttpHost("locahost"80);  
  14. cm.setMaxForRoute(new HttpRoute(localhost), 50);  
  15.    
  16. HttpClient httpClient = new DefaultHttpClient(cm);  
ConnManagerParams的功能被挪到了 ThreadSafeClientConnManager 和 HttpConnectionParams兩個類:


static  ( params) 
          Deprecated. use 
static int ( params) 
          Deprecated. use 
static long ( params) 
          Deprecated. use 
static void ( params,  connPerRoute) 
          Deprecated. use 
static void ( params, int maxTotalConnections) 
          Deprecated. use 
static void ( params, long timeout) 
          Deprecated. use 



參考:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9399028/viewspace-1824506/,如需轉載,請註明出處,否則將追究法律責任。

相關文章