HttpClient 教程 (二)

來源:互聯網
上載者:User

標籤:

第二章 串連管理

HttpClient有一個對串連初始化和終止,還有在活動串連上I/O操作的完整控制。而串連操作的很多方面可以使用一些參數來控制。

2.1 串連參數

這些參數可以影響串連操作:

  • ‘http.socket.timeout‘:定義了通訊端的毫秒級逾時時間(SO_TIMEOUT),這就是等待資料,換句話說,在兩個連續的資料包之間最大的閑置時間。如果逾時時間是0就解釋為是一個無限大的逾時時間。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設定,那麼讀取操作就不會逾時(無限大的逾時時間)。
  • ‘http.tcp.nodelay‘:決定了是否使用Nagle演算法。Nagle演算法視圖通過最小化發送的分組數量來節省頻寬。當應用程式希望降低網路延遲並提高效能時,它們可以關閉Nagle演算法(也就是開啟TCP_NODELAY)。資料將會更早發送,增加了頻寬消耗的成文。這個參數期望得到一個java.lang.Boolean類型的值。如果這個參數沒有被設定,那麼TCP_NODELAY就會開啟(無延遲)。
  • ‘http.socket.buffer-size‘:決定了內部通訊端緩衝使用的大小,來緩衝資料同時接收/傳輸HTTP報文。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設定,那麼HttpClient將會分配8192位元組的通訊端緩衝。
  • ‘http.socket.linger‘:使用指定的秒數拖延時間來設定SO_LINGER。最大的連線逾時值是平台指定的。值0暗示了這個選項是關閉的。值-1暗示了使用了JRE預設的。這個設定僅僅影響通訊端關閉操作。如果這個參數沒有被設定,那麼就假設值為-1(JRE預設)。
  • ‘http.connection.timeout‘:決定了直到串連建立時的毫秒級逾時時間。逾時時間的值為0解釋為一個無限大的時間。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設定,串連操作將不會逾時(無限大的逾時時間)。
  • ‘http.connection.stalecheck‘:決定了是否使用舊的串連檢查。當在一個串連之上執行一個請求而伺服器端的串連已經關閉時,關閉舊的串連檢查可能導致在獲得一個I/O錯誤風險時顯著的效能提升(對於每一個請求,檢查時間可以達到30毫秒)。這個參數期望得到一個java.lang.Boolean類型的值。出於效能的關鍵操作,檢查應該被關閉。如果這個參數沒有被設定,那麼舊的串連將會在每個請求執行之前執行。
  • ‘http.connection.max-line-length‘:決定了最大請求行長度的限制。如果設定為一個正數,任何HTTP請求行超過這個限制將會引發java.io.IOException異常。負數或零將會關閉這個檢查。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設定,那麼就不強制進行限制了。
  • ‘http.connection.max-header-count‘:決定了允許的最大HTTP頭部資訊數量。如果設定為一個正數,從資料流中獲得的HTTP頭部資訊數量超過這個限制就會引發java.io.IOException異常。負數或零將會關閉這個檢查。這個參數期望得到一個java.lang.Integer類型的值。如果這個參數沒有被設定,那麼就不
  • 強制進行限制了。
  • ‘http.connection.max-status-line-garbage‘:決定了在期望得到HTTP響應狀態行之前可忽略請求行的最大數量。使用HTTP/1.1持久性串連,這個問題產生的破碎的指令碼將會返回一個錯誤的Content-Length(有比指定的位元組更多的發送)。不幸的是,在某些情況下,這個不能在錯誤響應後來偵測,只能在下一次之前。所以HttpClient必須以這種方式跳過那些多餘的行。這個參數期望得到一個java.lang.Integer類型的值。0是不允許在狀態行之前的所有垃圾/空行。使用java.lang.Integer#MAX_VALUE來設定不限制的數字。如果這個參數沒有被設定那就假設是不限制的。
2.2 持久串連

從一個主機向另外一個建立串連的過程是相當複雜的,而且包含了兩個終端之間的很多包的交換,它是相當費時的。串連握手的開銷是很重要的,特別是對小量的HTTP報文。如果開啟的串連可以被重用來執行多次請求,那麼就可以達到很高的資料輸送量。

HTTP/1.1強調HTTP串連預設情況可以被重用於多次請求。HTTP/1.0相容的終端也可以使用相似的機制來明確地交流它們的偏好來保證串連處於活動狀態,也使用它來處理多個請求。HTTP代理也可以保持空閑串連處於一段時間的活動狀態,防止對相同目標主機的一個串連也許對隨後的請求需要。保持串連活動的能力通常被稱作持久性串連。HttpClient完全支援持久性串連。

2.3 HTTP連線路由

HttpClient能夠直接或通過路由建立串連到目標主機,這會涉及多個中間串連,也被稱為跳。HttpClient區分路由和普通串連,通道和分層。通道串連到目標主機的多個中間代理的使用也稱作是代理鏈。

普通路由由串連到目標或僅第一次的代理來建立。通道路由通過代理鏈到目標串連到第一通道來建立。沒有代理的路由不是通道的,分層路由通過已存在串連的分層協議來建立。協議僅僅可以在到目標的通道上或在沒有代理的直接連接上分層。

2.3.1 路由計算

RouteInfo介面代表關於最終涉及一個或多個中間步驟或跳的目標主機路由的資訊。HttpRoute是RouteInfo的具體實現,這是不能改變的(是不變的)。HttpTracker是可變的RouteInfo實現,由HttpClient在內部使用來跟蹤到最大路由目標的剩餘跳數。HttpTracker可以在成功執行向路由目標的下一跳之後更新。HttpRouteDirector是一個協助類,可以用來計算路由中的下一跳。這個類由HttpClient在內部使用。

HttpRoutePlanner是一個代表計算到基於執行內容到給定目標完整路由策略的介面。HttpClient附帶兩個預設的HttpRoutePlanner實現。ProxySelectorRoutePlanner是基於java.net.ProxySelector的。預設情況下,它會從系統屬性中或從運行應用程式的瀏覽器中選取JVM的代理設定。DefaultHttpRoutePlanner實現既不使用任何Java系統屬性,也不使用系統或瀏覽器的代理設定。它只基於HTTP如下面描述的參數計算路由。

2.3.2 安全HTTP串連

如果資訊在兩個不能由非認證的第三方進行讀取或修改的終端之間傳輸,HTTP串連可以被認為是安全的。SSL/TLS協議是用來保證HTTP傳輸安全使用最廣泛的技術。而其它加密技術也可以被使用。通常來說,HTTP傳輸是在SSL/TLS加密串連之上分層的。

2.4 HTTP路由參數這些參數可以影響路由計算:
  • ‘http.route.default-proxy‘:定義可以被不使用JRE設定的預設路由規劃者使用的代理主機。這個參數期望得到一個HttpHost類型的值。如果這個參數沒有被設定,那麼就會嘗試直接連接到目標。
  • ‘http.route.local-address‘:定義一個本地地址由所有預設路由規劃者來使用。有多個網路介面的機器中,這個參數可以被用於從串連源中選擇網路介面。這個參數期望得到一個java.net.InetAddress類型的值。如果這個參數沒有被設定,將會自動使用本地地址。
  • ‘http.route.forced-route‘:定義一個由所有預設路由規劃者使用的強制路由。代替了計算路由,給定的強制路由將會被返回,儘管它指向一個完全不同的目標主機。這個參數期望得到一個HttpRoute類型的值。如果這個參數沒有被設定,那麼就使用預設的規則建立串連到目標伺服器。
2.5 通訊端工廠

LayeredSocketFactory是SocketFactory介面的擴充。分層的通訊端工廠可HTTP串連內部使用java.net.Socket對象來處理資料線上路上的傳輸。它們依賴SocketFactory介面來建立,初始化和串連通訊端。這會使得HttpClient的使用者可以提供在運行時指定通訊端初始化代碼的應用程式。PlainSocketFactory是建立和初始化普通的(不加密的)通訊端的預設工廠。

建立通訊端的過程和串連到主機的過程是不成對的,所以通訊端在串連操作封鎖時可以被關閉。

PlainSocketFactory sf = PlainSocketFactory.getSocketFactory();Socket socket = sf.createSocket();HttpParams params = new BasicHttpParams();params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);sf.connectSocket(socket, "locahost", 8080, null, -1, params);
2.5.1 安全通訊端分層

LayeredSocketFactory是SocketFactory介面的擴充。分層的通訊端工廠可以建立在已經存在的普通通訊端之上的分層通訊端。通訊端分層主要通過代理來建立安全的通訊端。HttpClient附帶實現了SSL/TLS分層的SSLSocketFactory。請注意HttpClient不使用任何自訂加密功能。它完全依賴於標準的Java密碼學(JCE)和安全通訊端(JSEE)擴充。

2.5.2 SSL/TLS的定製

HttpClient使用SSLSocketFactory來建立SSL串連。SSLSocketFactory允許高度定製。它可以使用javax.net.ssl.SSLContext的執行個體作為參數,並使用它來建立定製SSL串連。

TrustManager easyTrustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {// 哦,這很簡單!}@Overridepublic void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException {//哦,這很簡單!}@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}};SSLContext sslcontext = SSLContext.getInstance("TLS");sslcontext.init(null, new TrustManager[] { easyTrustManager }, null);SSLSocketFactory sf = new SSLSocketFactory(sslcontext);SSLSocket socket = (SSLSocket) sf.createSocket();socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" });HttpParams params = new BasicHttpParams();params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);sf.connectSocket(socket, "locahost", 443, null, -1, params);
SSLSocketFactory的定製暗示出一定程度SSL/TLS協議概念的熟悉,這個詳細的解釋超出了本文檔的範圍。請參考Java的安全通訊端擴充[http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html],這是javax.net.ssl.SSLContext和相關工具的詳細描述。2.5.3 主機名稱驗證除了信任驗證和用戶端認證在SSL/TLS協議級上進行,一旦串連建立之後,HttpClient能可選地驗證目標主機名稱匹配儲存在伺服器的X.509認證中的名字。這個認證可以提供額外的伺服器信任材料的真實保證。X509主機名稱驗證介面代表了主機名稱驗證的策略。HttpClient附帶了3個X509主機名稱驗證器。很重要的一點是:主機名稱驗證不應該混淆SSL信任驗證。
  • StrictHostnameVerifier:嚴格的主機名稱驗證在Sun Java 1.4,Sun Java 5和Sun Java 6中是相同的。而且也非常接近IE6。這個實現似乎是相容RFC 2818處理萬用字元的。主機名稱必須匹配第一個CN或任意的subject-alt。在CN和其它任意的subject-alt中可能會出現萬用字元。
  • BrowserCompatHostnameVerifier:主機名稱驗證器和Curl和Firefox的工作方式是相同的。主機名稱必須匹配第一個CN或任意的subject-alt。在CN和其它任意的subject-alt中可能會出現萬用字元。BrowserCompatHostnameVerifier和StrictHostnameVerifier的唯一不同是使用BrowserCompatHostnameVerifier匹配所有子域的萬用字元(比如”*.foo.com”),包括”a.b.foo.com”。
  • AllowAllHostnameVerifier:這個主機名稱驗證器基本上是關閉主機名稱驗證的。這個實現是一個空操作,而且不會拋出javax.net.ssl.SSLException異常。

每一個預設的HttpClient使用BrowserCompatHostnameVerifier的實現。如果需要的話,它可以指定不同的主機名稱驗證器實現。

SSLSocketFactory sf = new SSLSocketFactory(SSLContext.getInstance("TLS"));sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
2.6 協議模式

Scheme類代表了一個協議模式,比如“http”或“https”同時包含一些協議屬性,比如預設連接埠,用來為給定協議建立java.net.Socket執行個體的通訊端工廠。SchemeRegistry類用來維持一組Scheme,當去通過請求URI建立串連時,HttpClient可以從中選擇:

Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);SSLSocketFactory sf = new SSLSocketFactory(SSLContext.getInstance("TLS"));sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);Scheme https = new Scheme("https", sf, 443);SchemeRegistry sr = new SchemeRegistry();sr.register(http);sr.register(https);
2.7 HttpClient代理配置

儘管HttpClient瞭解複雜的路由模式和代理鏈,它僅支援簡單直接的或開箱的跳式代理串連。

告訴HttpClient通過代理去串連到目標主機的最簡單方式是通過設定預設的代理參數:

DefaultHttpClient httpclient = new DefaultHttpClient();HttpHost proxy = new HttpHost("someproxy", 8080);httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

也可以構建HttpClient使用標準的JRE代理選取器來獲得代理資訊:

DefaultHttpClient httpclient = new DefaultHttpClient();ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(httpclient.getConnectionManager().getSchemeRegistry(),ProxySelector.getDefault());httpclient.setRoutePlanner(routePlanner);

另外一種選擇,可以提供一個定製的RoutePlanner實現來獲得HTTP路由計算處理上的複雜的控制:

DefaultHttpClient httpclient = new DefaultHttpClient();httpclient.setRoutePlanner(new HttpRoutePlanner() {public HttpRoute determineRoute(HttpHost target,HttpRequest request,HttpContext context) throws HttpException {return new HttpRoute(target, null, new HttpHost("someproxy", 8080),"https".equalsIgnoreCase(target.getSchemeName()));}});
2.8 HTTP連線管理員2.8.1 串連操作器

串連操作是用戶端的低層通訊端或可以通過外部實體,通常稱為串連操作的被操作的狀態的串連。OperatedClientConnection介面擴充了HttpClientConnection介面而且定義了額外的控制串連通訊端的方法。ClientConnectionOperator介面代表了建立執行個體和更新那些對象低層通訊端的策略。實作類別最有可能利用SocketFactory來建立java.net.Socket執行個體。ClientConnectionOperator介面可以讓HttpClient的使用者提供一個串連操作的定製策略和提供可選實現OperatedClientConnection介面的能力。

2.8.2 管理串連和連線管理員

HTTP串連是複雜的,有狀態的,線程不安全的對象需要正確的管理以便正確地執行功能。HTTP串連在同一時間僅僅只能由一個執行線程來使用。HttpClient採用一個特殊實體來管理訪問HTTP串連,這被稱為HTTP連線管理員,代表了ClientConnectionManager介面。一個HTTP連線管理員的目的是作為工廠服務於新的HTTP串連,管理持久串連和同步訪問持久串連來確保同一時間僅有一個線程可以訪問一個串連。

內部的HTTP連線管理員和OperatedClientConnection執行個體一起工作,但是它們為服務消耗器ManagedClientConnection提供執行個體。ManagedClientConnection扮演串連之上管理狀態控制所有I/O操作的OperatedClientConnection執行個體的封裝器。它也抽象通訊端操作,提供開啟和更新去建立路由通訊端便利的方法。ManagedClientConnection執行個體瞭解產生它們到連線管理員的連結,而且基於這個事實,當不再被使用時,它們必須返回到管理器。ManagedClientConnection類也實現了ConnectionReleaseTrigger介面,可以被用來觸發釋放串連返回給管理器。一旦釋放串連操作被觸發了,被封裝的串連從ManagedClientConnection封裝器中脫離,OperatedClientConnection執行個體被返回給管理器。儘管服務消耗器仍然持有ManagedClientConnection執行個體的引用,它也不再去執行任何I/O操作或有意無意地改變的OperatedClientConnection狀態。

這裡有一個從連線管理員中擷取串連的樣本:

HttpParams params = new BasicHttpParams();Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);SchemeRegistry sr = new SchemeRegistry();sr.register(http);ClientConnectionManager connMrg = new SingleClientConnManager(params, sr);// 請求新串連。這可能是一個很長的過程。ClientConnectionRequest connRequest = connMrg.requestConnection(new HttpRoute(new HttpHost("localhost", 80)), null);// 等待串連10秒ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS);try {// 用串連在做有用的事情。當完成時釋放串連。conn.releaseConnection();} catch (IOException ex) {// 在I/O error之上終止串連。conn.abortConnection();throw ex;}

如果需要,串連請求可以通過調用來ClientConnectionRequest#abortRequest()方法過早地中斷。這會解鎖在ClientConnectionRequest#getConnection()方法中被阻止的線程。

一旦響應內容被完全消耗後,BasicManagedEntity封裝器類可以用來保證自動釋放低層的串連。HttpClient內部使用這個機制來實現透明地對所有從HttpClient#execute()方法中獲得響應釋放串連:

ClientConnectionRequest connRequest = connMrg.requestConnection(new HttpRoute(new HttpHost("localhost", 80)), null);ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS);try {BasicHttpRequest request = new BasicHttpRequest("GET", "/");conn.sendRequestHeader(request);HttpResponse response = conn.receiveResponseHeader();conn.receiveResponseEntity(response);HttpEntity entity = response.getEntity();if (entity != null) {BasicManagedEntity managedEntity = new BasicManagedEntity(entity, conn, true);// 替換實體response.setEntity(managedEntity);}// 使用響應對象做有用的事情。當響應內容被消耗後這個串連將會自動釋放。} catch (IOException ex) {//在I/O error之上終止串連。conn.abortConnection();throw ex;}
2.8.3 簡單連線管理員

SingleClientConnManager是一個簡單的連線管理員,在同一時間它僅僅維護一個串連。儘管這個類是安全執行緒的,但它應該被用於一個執行線程。SingleClientConnManager對於同一路由的後續請求會盡量重用串連。而如果持久串連的路由不匹配串連請求的話,它也會關閉存在的串連之後對給定路由再開啟一個新的。如果串連已經被分配,將會拋出java.lang.IllegalStateException異常。

對於每個預設串連,HttpClient使用SingleClientConnManager。

2.8.4 串連池管理器

ThreadSafeClientConnManager是一個複雜的實現來管理用戶端串連池,它也可以從多個執行線程中服務串連請求。對每個基本的路由,串連都是池管理的。對於路由的請求,管理器在池中有可用的持久性串連,將被從池中租賃串連服務,而不是建立一個新的串連。

ThreadSafeClientConnManager維護每個基本路由的最大串連限制。每個預設的實現對每個給定路由將會建立不超過兩個的並發串連,而總共也不會超過20個串連。對於很多真實的應用程式,這個限制也證明很大的制約,特別是他們在服務中使用HTTP作為傳輸協議。串連限制,也可以使用HTTP參數來進行調整。

這個樣本展示了串連池參數是如何來調整的:

HttpParams params = new BasicHttpParams();// 增加最大串連到200ConnManagerParams.setMaxTotalConnections(params, 200);// 增加每個路由的預設最大串連到20ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);// 對localhost:80增加最大串連到50HttpHost localhost = new HttpHost("locahost", 80);connPerRoute.setMaxForRoute(new HttpRoute(localhost), 50);ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);SchemeRegistry schemeRegistry = new SchemeRegistry();schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);HttpClient httpClient = new DefaultHttpClient(cm, params);
2.8.5 連線管理員關閉

當一個HttpClient執行個體不再需要時,而且即將走出使用範圍,那麼關閉連線管理員來保證由管理器保持活動的所有串連被關閉,由串連分配的系統資源被釋放是很重要的。

DefaultHttpClient httpclient = new DefaultHttpClient();HttpGet httpget = new HttpGet("http://www.google.com/");HttpResponse response = httpclient.execute(httpget);HttpEntity entity = response.getEntity();System.out.println(response.getStatusLine());if (entity != null) {entity.consumeContent();}httpclient.getConnectionManager().shutdown();
2.9 串連管理參數這些是可以用於定製標準HTTP連線管理員實現的參數:
  • ‘http.conn-manager.timeout‘:定義了當從ClientConnectionManager中檢索ManagedClientConnection執行個體時使用的毫秒級的逾時時間。這個參數期望得到一個java.lang.Long類型的值。如果這個參數沒有被設定,串連請求就不會逾時(無限大的逾時時間)。
  • ‘http.conn-manager.max-per-route‘:定義了每個路由串連的最大數量。這個限制由用戶端連線管理員來解釋,而且應用於獨立的管理器執行個體。這個參數期望得到一個ConnPerRoute類型的值。
  • ‘http.conn-manager.max-total‘:定義了總共串連的最大數目。這個限制由用戶端連線管理員來解釋,而且應用於獨立的管理器執行個體。這個參數期望得到一個java.lang.Integer類型的值。
2.10 多線程執行請求

當配備串連池管理器時,比如ThreadSafeClientConnManager,HttpClient可以同時被用來執行多個請求,使用多線程執行。

ThreadSafeClientConnManager將會分配基於它的配置的串連。如果對於給定路由的所有串連都被租出了,那麼串連的請求將會阻塞,直到一個串連被釋放回串連池。它可以通過設定‘http.conn-manager.timeout‘為一個正數來保證連線管理員不會在串連請求執行時無限期的被阻塞。如果串連請求不能在給定的時間周期內被響應,將會拋出ConnectionPoolTimeoutException異常。

HttpParams params = new BasicHttpParams();SchemeRegistry schemeRegistry = new SchemeRegistry();schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);HttpClient httpClient = new DefaultHttpClient(cm, params);// 執行GET方法的URIString[] urisToGet = {"http://www.domain1.com/","http://www.domain2.com/","http://www.domain3.com/","http://www.domain4.com/"};// 為每個URI建立一個線程GetThread[] threads = new GetThread[urisToGet.length];for (int i = 0; i < threads.length; i++) {HttpGet httpget = new HttpGet(urisToGet[i]);threads[i] = new GetThread(httpClient, httpget);}
// 開始執行線程for (int j = 0; j < threads.length; j++) {threads[j].start();}// 合并線程for (int j = 0; j < threads.length; j++) {threads[j].join();} static class GetThread extends Thread {private final HttpClient httpClient;private final HttpContext context;private final HttpGet httpget;public GetThread(HttpClient httpClient, HttpGet httpget) {this.httpClient = httpClient;this.context = new BasicHttpContext();this.httpget = httpget;}@Overridepublic void run() {try {HttpResponse response = this.httpClient.execute(this.httpget, this.context);HttpEntity entity = response.getEntity();if (entity != null) {// 對實體做些有用的事情...// 保證串連能釋放回管理器entity.consumeContent();}} catch (Exception ex) {this.httpget.abort();}}}
2.11 串連收回策略

一個經典的阻塞I/O模型的主要缺點是網路通訊端僅當I/O操作阻塞時才可以響應I/O事件。當一個串連被釋放返回管理器時,它可以被保持活動狀態而卻不能監控通訊端的狀態和響應任何I/O事件。如果串連在伺服器端關閉,那麼用戶端串連也不能去偵測串連狀態中的變化和關閉本端的通訊端去作出適當響應。

HttpClient通過測試連接是否是過時的來嘗試去減輕這個問題,這已經不再有效了,因為它已經在伺服器端關閉了,之前使用執行HTTP請求的串連。過時的串連檢查也並不是100%的穩定,反而對每次請求執行還要增加10到30毫秒的開銷。唯一可行的而不涉及到每個對空閑串連的通訊端模型線程解決方案,是使用專用的監控線程來收回因為長時間不活動而被認為是到期的串連。監控線程可以周期地調用ClientConnectionManager#closeExpiredConnections()方法來關閉所有到期的串連,從串連池中收回關閉的串連。它也可以選擇性調用ClientConnectionManager#closeIdleConnections()方法來關閉所有已經空閑超過給定時間周期的串連。

public static class IdleConnectionMonitorThread extends Thread {private final ClientConnectionManager connMgr;private volatile boolean shutdown;public IdleConnectionMonitorThread(ClientConnectionManager connMgr) {super();this.connMgr = connMgr;}@Overridepublic void run() {try {while (!shutdown) {synchronized (this) {wait(5000);// 關閉到期串連connMgr.closeExpiredConnections();// 可選地,關閉空閑超過30秒的串連connMgr.closeIdleConnections(30, TimeUnit.SECONDS);}}} catch (InterruptedException ex) {// 終止}}public void shutdown() {shutdown = true;synchronized (this) {notifyAll();}}}
2.12 串連保持活動的策略

HTTP規範沒有確定一個持久串連可能或應該保持活動多長時間。一些HTTP伺服器使用非標準的頭部資訊Keep-Alive來告訴用戶端它們想在伺服器端保持串連活動的周期秒數。如果這個資訊可用,HttClient就會利用這個它。如果頭部資訊Keep-Alive在響應中不存在,HttpClient假設串連無限期的保持活動。然而許多現實中的HTTP伺服器配置了在特定不活動周期之後丟掉持久串連來儲存系統資源,往往這是不通知用戶端的。如果預設的策略證明是過於樂觀的,那麼就會有人想提供一個定製的保持活動策略。

 

DefaultHttpClient httpclient = new DefaultHttpClient();httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {public long getKeepAliveDuration(HttpResponse response, HttpContext context) {// 兌現‘keep-alive‘頭部資訊HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));while (it.hasNext()) {HeaderElement he = it.nextElement();String param = he.getName();String value = he.getValue();if (value != null && param.equalsIgnoreCase("timeout")) {try {return Long.parseLong(value) * 1000;} catch(NumberFormatException ignore) {}}}HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {// 只保持活動5秒return 5 * 1000;} else {// 否則保持活動30秒return 30 * 1000;}}});

HttpClient 教程 (二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.