天天看點

HttpClient容易忽視的細節——連接配接關閉

[java] view plaincopy  

  1. java.net.SocketException: Too many open files  
  2.  at java.net.Socket.createImpl(Socket.java:397)  
  3.  at java.net.Socket.<init>(Socket.java:371)  
  4.  at java.net.Socket.<init>(Socket.java:249)  
  5.  at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)  
  6.  at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)  
  7.  at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)  
  8.  at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)  
  9.  at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)  
  10.  at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)  
  11.  at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)  
  12.  at com.thhc.mylegist.baidu.BaiDuHttpFactory.getQuestionList(BaiDuHttpFactory.java:284)  
  13.  at com.thhc.mylegist.baidu.BaiDuTask.run(BaiDuTask.java:21)  
  14.  at java.util.TimerThread.mainLoop(Timer.java:512)  
  15.  at java.util.TimerThread.run(Timer.java:462)  

下面是轉載的内容:

[java] view plaincopy  

  1. Java代碼   
  2. 1.HttpClient client = new HttpClient();     
  3. 2.HttpMethod method = new GetMethod("http://www.apache.org");     
  4. 3.try {     
  5. 4.  client.executeMethod(method);     
  6. 5.  byte[] responseBody = null;     
  7. 6.       
  8. 7.  responseBody = method.getResponseBody();     
  9. 8.       
  10. 9.} catch (HttpException e) {     
  11. 10.  // TODO Auto-generated catch block     
  12. 11.  e.printStackTrace();     
  13. 12.} catch (IOException e) {     
  14. 13.  // TODO Auto-generated catch block     
  15. 14.  e.printStackTrace();     
  16. 15.}finally{     
  17. 16.  method.releaseConnection();     
  18. 17.       
  19. 18.}    
  20. HttpClient client = new HttpClient();  
  21. HttpMethod method = new GetMethod("http://www.apache.org");  
  22. try {  
  23.   client.executeMethod(method);  
  24.   byte[] responseBody = null;  
  25.   responseBody = method.getResponseBody();  
  26. } catch (HttpException e) {  
  27.   // TODO Auto-generated catch block  
  28.   e.printStackTrace();  
  29. } catch (IOException e) {  
  30.   // TODO Auto-generated catch block  
  31.   e.printStackTrace();  
  32. }finally{  
  33.   method.releaseConnection();  
  34. }  
  35. 大部分人使用HttpClient都是使用類似上面的事例代碼,包括Apache官方的例子也是如此。最近我在使用HttpClient是發現一次循環發送大量請求到伺服器會導緻APACHE伺服器的連結被占滿,後續的請求便排隊等待。   
  36. 我伺服器端APACHE的配置   
  37. Java代碼   
  38. 1.Timeout 30    
  39. 2.KeepAlive On   #表示伺服器端不會主動關閉連結     
  40. 3.MaxKeepAliveRequests 100    
  41. 4.KeepAliveTimeout 180     
  42. Timeout 30  
  43. KeepAlive On   #表示伺服器端不會主動關閉連結  
  44. MaxKeepAliveRequests 100  
  45. KeepAliveTimeout 180   
  46. 是以這樣的配置就會導緻每個連結至少要過180S才會被釋放,這樣在大量請求通路時就必然會造成連結被占滿,請求等待的情況。   
  47. 在通過DEBUH後發現HttpClient在method.releaseConnection()後并沒有把連結關閉,這個方法隻是将連結傳回給connection manager。如果使用HttpClient client = new HttpClient()執行個體化一個HttpClient connection manager預設實作是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有個構造函數如下   
  48. Java代碼   
  49. 1.    
  50. 10.public SimpleHttpConnectionManager(boolean alwaysClose) {     
  51. 11.    super();     
  52. 12.    this.alwaysClose = alwaysClose;     
  53. 13.}    
  54. public SimpleHttpConnectionManager(boolean alwaysClose) {  
  55.     super();  
  56.     this.alwaysClose = alwaysClose;  
  57. }  
  58. 看方法注釋我們就可以看到如果alwaysClose設為true在連結釋放之後connection manager 就會關閉鍊。在我們HttpClient client = new HttpClient()這樣執行個體化一個client時connection manager是這樣被執行個體化的   
  59. Java代碼   
  60. 1.this.httpConnectionManager = new SimpleHttpConnectionManager();    
  61. this.httpConnectionManager = new SimpleHttpConnectionManager();  
  62. 是以alwaysClose預設是false,connection是不會被主動關閉的,是以我們就有了一個用戶端關閉連結的方法。   
  63. 方法一:   
  64. 把事例代碼中的第一行執行個體化代碼改為如下即可,在method.releaseConnection();之後connection manager會關閉connection 。   
  65. Java代碼   
  66. 1.HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );    
  67. HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  
  68. 方法二:   
  69. 執行個體化代碼使用:HttpClient client = new HttpClient();   
  70. 在method.releaseConnection();之後加上   
  71. Java代碼   
  72. 1.((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();    
  73. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  
  74. shutdown源代碼很簡單,看了一目了然   
  75. Java代碼   
  76. 1.public void shutdown() {     
  77. 2.    httpConnection.close();     
  78. 3.}    
  79. public void shutdown() {  
  80.     httpConnection.close();  
  81. }  
  82. 方法三:   
  83. 執行個體化代碼使用:HttpClient client = new HttpClient();   
  84. 在method.releaseConnection();之後加上   
  85. client.getHttpConnectionManager().closeIdleConnections(0);此方法源碼代碼如下:   
  86. Java代碼   
  87. 1.public void closeIdleConnections(long idleTimeout) {     
  88. 2.    long maxIdleTime = System.currentTimeMillis() - idleTimeout;     
  89. 3.    if (idleStartTime <= maxIdleTime) {     
  90. 4.        httpConnection.close();     
  91. 5.    }     
  92. 6.}    
  93. public void closeIdleConnections(long idleTimeout) {  
  94.     long maxIdleTime = System.currentTimeMillis() - idleTimeout;  
  95.     if (idleStartTime <= maxIdleTime) {  
  96.         httpConnection.close();  
  97.     }  
  98. }  
  99. 将idleTimeout設為0可以確定連結被關閉。   
  100. 以上這三種方法都是有用戶端主動關閉TCP連結的方法。下面再介紹由伺服器端自動關閉連結的方法。   
  101. 方法四:   
  102. 代碼實作很簡單,所有代碼就和最上面的事例代碼一樣。隻需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP頭的設定即可   
  103. Java代碼   
  104. 1.method.setRequestHeader("Connection", "close");    
  105. method.setRequestHeader("Connection", "close");  
  106. 看一下HTTP協定中關于這個屬性的定義:   
  107. 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,   
  108.        Connection: close   
  109. 現在再說一下用戶端關閉連結和伺服器端關閉連結的差別。如果采用用戶端關閉連結的方法,在用戶端的機器上使用netstat –an指令會看到很多TIME_WAIT的TCP連結。如果伺服器端主動關閉連結這中情況就出現在伺服器端。   
  110. 參考WIKI上的說明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions   
  111. 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.   
  112. TIME_WAIT的狀态會出現在主動關閉連結的這一端。TCP協定中TIME_WAIT狀态主要是為了保證資料的完整傳輸。具體可以參考此文檔:   
  113. http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7   
  114. 另外強調一下使用上面這些方法關閉連結是在我們的應用中明确知道不需要重用連結時可以主動關閉連結來釋放資源。如果你的應用是需要重用連結的話就沒必要這麼做,使用原有的連結還可以提供性能。   

評論:

[java] view plaincopy  

  1. 8 樓 javatar 2008-11-23   引用   
  2. 官方文檔有建議使用空閑連接配接檢查線程:   
  3. Java代碼   
  4. 1.import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;     
  5. 2.// 建立線程     
  6. 3.IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread();     
  7. 4.// 注冊連接配接管理器     
  8. 5.thread.addConnectionManager(httpClient.getHttpConnectionManager());     
  9. 6.// 啟動線程     
  10. 7.thread.start();     
  11. 8.// 在最後,關閉線程     
  12. 9.thread.shutdown();    
  13. import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;  
  14. // 建立線程  
  15. IdleConnectionTimeoutThread thread = new IdleConnectionTimeoutThread();  
  16. // 注冊連接配接管理器  
  17. thread.addConnectionManager(httpClient.getHttpConnectionManager());  
  18. // 啟動線程  
  19. thread.start();  
  20. // 在最後,關閉線程  
  21. thread.shutdown();  
  22. 7 樓 sdh5724 2008-10-29   引用   
  23. 樓上的說法是對的, 但是, 性能來說, 是不合适的.   
  24. 首先, 我想問你, 既然是輪詢, 為什麼HttpURLConnection要每次建立.  既然是個持續的過程, 就不應該每次HttpURLConnection對象重新建立, 你應該重複使用這個對象. HttpURLConnection會持有連接配接的, 如果斷開, 我記得他會去嘗試連接配接.   
  25. 6 樓 SeanHe 2008-10-28   引用   
  26. kjah 寫道  
  27. 但使用netstat發現很多TIME_WAIT,時間長點後會出現端口都被占用的狀況 address already in use :connect,使用HttpURLConnection.disconnect()也沒有效果。  
  28. 如果你是應為用戶端出現很多的TIME_WAIT造成端口占用,你不妨試一下“方法四”在HTTP請求頭上設定"Connection", "close"這樣伺服器會來主動關閉連結,這樣就不會在用戶端産生很多的TIME_WAIT  
  29. 5 樓 kjah 2008-10-28   引用   
  30. 我是google到這裡的。。。   
  31. 您對HttpClient 的關閉分析的很透徹   
  32. 請教HttpURLConnection和URLConnection怎麼關閉連結?   
  33. 我需要輪詢一個網址,但使用netstat發現很多TIME_WAIT,時間長點後會出現端口都被占用的狀況 address already in use :connect,使用HttpURLConnection.disconnect()也沒有效果。  
  34. 4 樓 fly_ever 2008-09-11   引用   
  35. 引用  
  36. 另外強調一下使用上面這些方法關閉連結是在我們的應用中明确知道不需要重用連結時可以主動關閉連結來釋放資源。如果你的應用是需要重用連結的話就沒必要這麼做,使用原有的連結還可以提供性能。   
  37. 正如你所說,實際上是隻有并發量很高的時候才會發生連結占滿的情況。   
  38. 而如果沒有這麼高的并發量,我們沒必要去實作代碼來關閉連接配接。   
  39. 因為我們在代碼中實作主動關閉的功能的同時,也喪失了性能上的優勢。   
  40. 是以,我覺得遇到這種情況,應該把設定中的keepAliveTimeout調低,顯得更好一些。   
  41. Java代碼   
  42. 1.1. Timeout 30       
  43. 2.2. KeepAlive On   #表示伺服器端不會主動關閉連結       
  44. 3.3. MaxKeepAliveRequests 100       
  45. 4.4. KeepAliveTimeout 180       
  46.    1. Timeout 30    
  47.    2. KeepAlive On   #表示伺服器端不會主動關閉連結    
  48.    3. MaxKeepAliveRequests 100    
  49.    4. KeepAliveTimeout 180     
  50. 不過,這裡提供的HttpClient關閉的詳細資訊,還是很有價值的。  
  51. 3 樓 jorsef 2008-09-10   引用   
  52. 受用,非常感謝  
  53. 2 樓 SeanHe 2008-09-06   引用   
  54. HttpClient client = new HttpClient(); 如果這樣進行執行個體化,預設使用SimpleHttpConnectionManager作為connection manager,SimpleHttpConnectionManager沒有連接配接池,隻管理一個連接配接  
  55. 1 樓 JavaTestJava 2008-09-05   引用   
  56. 方法二:   
  57. 執行個體化代碼使用:HttpClient client = new HttpClient();   
  58. 在method.releaseConnection();之後加上   
  59. Java代碼   
  60. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();    
  61. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();   
  62. shutdown源代碼很簡單,看了一目了然   
  63. Java代碼   
  64. public void shutdown() {     
  65.     httpConnection.close();     
  66. }    
  67. public void shutdown() {   
  68.     httpConnection.close();   
  69. }   
  70. 方法二中的httpConnection.close();這裡沒有參數麼?   
  71. httpConnection為預設全局的那個連接配接?