日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

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

 關(guān)平藏書 2019-05-13

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

目前使用最多的版本還是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();
    }  
  }
}

大部分人也是以這個(gè)為規(guī)范來使用的,但是注意有一段關(guān)于“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 這個(gè)連接可以重用了。

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

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

查看源碼,找到HttpClient 的構(gòu)造方法,有一個(gè)可以指定HttpConnectionManager ,然后這個(gè)HttpConnectionManager 又有一個(gè)實(shí)現(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 的默認(rèn)值是false ,在釋放后連接并不總是會(huì)關(guān)閉。

所以,必須

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ā)送大量請(qǐng)求到服務(wù)器會(huì)導(dǎo)致APACHE服務(wù)器的鏈接被占滿,后續(xù)的請(qǐng)求便排隊(duì)等待。
我服務(wù)器端APACHE的配置
Java代碼  收藏代碼
  1. Timeout 30  
  2. KeepAlive On   #表示服務(wù)器端不會(huì)主動(dòng)關(guān)閉鏈接  
  3. MaxKeepAliveRequests 100  
  4. KeepAliveTimeout 180   

因此這樣的配置就會(huì)導(dǎo)致每個(gè)鏈接至少要過180S才會(huì)被釋放,這樣在大量請(qǐng)求訪問時(shí)就必然會(huì)造成鏈接被占滿,請(qǐng)求等待的情況。
在通過DEBUH后發(fā)現(xiàn)HttpClient在method.releaseConnection()后并沒有把鏈接關(guān)閉,這個(gè)方法只是將鏈接返回給connection manager。如果使用HttpClient client = new HttpClient()實(shí)例化一個(gè)HttpClient connection manager默認(rèn)實(shí)現(xiàn)是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有個(gè)構(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設(shè)為true在鏈接釋放之后connection manager 就會(huì)關(guān)閉鏈。在我們HttpClient client = new HttpClient()這樣實(shí)例化一個(gè)client時(shí)connection manager是這樣被實(shí)例化的
Java代碼  收藏代碼
  1. this.httpConnectionManager = new SimpleHttpConnectionManager();  

因此alwaysClose默認(rèn)是false,connection是不會(huì)被主動(dòng)關(guān)閉的,因此我們就有了一個(gè)客戶端關(guān)閉鏈接的方法。
方法一:
把事例代碼中的第一行實(shí)例化代碼改為如下即可,在method.releaseConnection();之后connection manager會(huì)關(guān)閉connection 。
Java代碼  收藏代碼
  1. HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  

方法二:
實(shí)例化代碼使用:HttpClient client = new HttpClient();
在method.releaseConnection();之后加上
Java代碼  收藏代碼
  1. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

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

方法三:
實(shí)例化代碼使用: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設(shè)為0可以確保鏈接被關(guān)閉。
以上這三種方法都是有客戶端主動(dòng)關(guān)閉TCP鏈接的方法。下面再介紹由服務(wù)器端自動(dòng)關(guān)閉鏈接的方法。
方法四:
代碼實(shí)現(xiàn)很簡(jiǎn)單,所有代碼就和最上面的事例代碼一樣。只需要在HttpMethod method = new GetMethod("http://www.");加上一行HTTP頭的設(shè)置即可
Java代碼  收藏代碼
  1. method.setRequestHeader("Connection", "close");  

看一下HTTP協(xié)議中關(guān)于這個(gè)屬性的定義:
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)在再說一下客戶端關(guān)閉鏈接和服務(wù)器端關(guān)閉鏈接的區(qū)別。如果采用客戶端關(guān)閉鏈接的方法,在客戶端的機(jī)器上使用netstat –an命令會(huì)看到很多TIME_WAIT的TCP鏈接。如果服務(wù)器端主動(dòng)關(guān)閉鏈接這中情況就出現(xiàn)在服務(wù)器端。
參考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)會(huì)出現(xiàn)在主動(dòng)關(guā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
另外強(qiáng)調(diào)一下使用上面這些方法關(guān)閉鏈接是在我們的應(yīng)用中明確知道不需要重用鏈接時(shí)可以主動(dòng)關(guān)閉鏈接來釋放資源。如果你的應(yīng)用是需要重用鏈接的話就沒必要這么做,使用原有的鏈接還可以提供性能。

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多