1.2 HttpClient接口java
HttpClient接口表明了HTTP請求執行最重要的約定.它規定了請求執行過程無任何和限制或者特定的細節以及審閱鏈接管理,狀態管理,認證和重定向處理的實現等細節.這使得裝飾接口附加功能更容易好比響應內容的緩存.緩存
一般HttpClient的實現只是做爲樣子,大量特殊目的的處理器或策略接口的實現來處理HTTP協議具體的各個方面好比重定向或者認證的處理或者決定鏈接持續的時間.這使得用戶能夠替換某些默認的實現.安全
ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(){ public long getKeepAliveDuration(HttpResponse response, HttpContext context){ long keepAlive = super.getKeepAliveDuration(response,context); if(keepAlive == -1) { //Keep connections alive 5 seconds if keep-alive value //has not be explicitly set by the server keepAlive = 5000; } return keepAlive; } }; CloseableHttpClient httpclient = HttpClients.custom().setKeepAliveStrategy(keepAliveStrat).build();
1.2.1 HttpClient線程安全服務器
HttpClient實現能夠認爲是線程安全的.推薦對於屢次請求的執行使用該類的同一個實例.session
1.2.2 併發
HttpClient資源的解除ide
當CloseableHttpClient實例不在使用且超出該示例相關的鏈接管理的做用域是必須經過調用CloseableHttpClient#close()方法關閉.工具
CloseableHttpClient httpclient = HttpClients.createDefault(); try { <...> } finally { httpclient.close(); }
1.3 HTTP執行上下文ui
最初的HTTP被設計成無狀態的,響應請求導向協議.然而真實環境中的應用常常須要經過多個邏輯上關聯的請求響應交互保存狀態信息.爲了是程序可以保持處理狀態HttpClient容許HTTP請求在特定的上下文中執行.當同一個上下文被一系列的請求複用,邏輯關聯的請求將共享一個邏輯上的session.HTTP上下文功能相似於java.util.Map<String,Object>.一個簡單的鍵值對集合.應用程序能夠在執行時填充上下文或者在執行完成後檢查上下文.線程
HttpContext能夠包含任意的對象所以在多個線程共享是不保證線程安全.推薦每一個線程位置本身的上下文.
在HTTP請求執行過程當中HttpClient添加如下上下文屬性:
HttpConnection 表明實際抵達目標服務器的鏈接.
HttpHost 表明鏈接的目標
HttpRoute 表明完整的鏈接路由
HttpRequest 表明實際的HTTP請求.最終的HttpRequest對像始終表示消息已被髮送到目標服務器的狀態.默認的HTTP/1.0和HTTP1.1使用相對請求URI.可是若是請求經過非隧道模式的代理髮送則URI是絕對的.
HttpResponse 表明示例的HTTP響應
java.lang.Boolean 表明請求是否徹底的傳送到鏈接的目標.
RequestConfig 表明實際的請求設置.
java.util.List<URI> 表明在請求執行過程當中接收到的全部重定向位置集合
可使用HttpClientContext適配類來簡化上下文之間的差別.
HttpContext context = <...>; HttpClientContext clientContext = HttpClientContext.adapt(context); HttpHost target = clientContext.getTargetHost(); HttpRequest request = clientContext.getRequest(); HttpResponse response = clientContext.getResponse(); RequestConfig config = clientContext.getRequestConfig();
表明一個邏輯上相關的會話的請求序列應該用相同的HttpContext實例執行以確保上下文和狀態信息在請求間的共享.
在接下來的示例中由初始請求配置將在執行上下文中保持並共享給其餘相同上下文中的請求.
CloseableHttpClient httpclient = HttpClients.createDefault(); RequestConfig requestConfig = ReuestConfig.custom().setSocketTimeout(1000).setConnectTimeout(1000).build(); HttpGet httpget1 = new HttpGet(); httpget1.setConfig(requestConfig); CloseableHttpResponse response1 = httpclient.execute(httpget1,context); try { HttpEntity entity1 = response1.getEntity(); } finally { response1.close(); } HttpGet httpget2 = new HttpGet(); httpget2.setConfig(requestConfig); CloseableHttpResponse response1 = httpclient.execute(httpget2,context); try { HttpEntity entity1 = response1.getEntity(); } finally { response1.close(); }
1.4 HTTP協議攔截器
HTTP請求攔截器是HTTP協議具體的常規實現.通常協議攔截預期做用在傳入消息一個特定的頭部或一組相關的頭部.協議攔截器也能夠操做消息封裝的實體內容-傳輸內容的壓縮和解壓是最好的例子.這一般可使用裝飾器模式包裝原有實體來完成.多個協議攔截器能夠被組成一個邏輯單元.
協議攔截器能夠經過共享信息合做-好比處理狀態-經過HTTP執行上下文.協議攔截器能夠存儲一個或一系列請求的處理狀態.一般這種攔截器的執行順序並不重要,只要他們不依賴執行上下文的狀態.若是協議攔截具備相互依賴關係而必須以特定順序執行,則應保證添加的順序與預期執行的順序一致.
協議攔截器必須爲線程安全的實現.與servlet相似,協議攔截器不該該使用實例變量除非對這些變量進行同步.
下面的例子展現上下文如何用於維持一系列請求的處理狀態:
CloseableHttpClient httpclient = HttpClients.custom().addInterceptorLast(new HttpRequestInterceptor(){ public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException{ AtomicInteger count = (AtomicInteger)context.getAttribute("count"); } }).build(); AtomicInteger count = new AtomicInteger(1); HttpClientContext localContext = HttpClientContext.create(); localContext.setAttribute("count",count); HttpGet httpget = new HttpGer(""); for(int i = 0; i < 10; i++){ CloseableHttpResponse response = httpclient.execute(httpget, localContext); try{ HttpEntity entity = response.getEntity(); } finally { response.close(); } }
1.5 異常處理
HTTP協議的處理這可能拋出兩種類型的異常:java.io.IOException 表示I/O失敗好比套接字超時或套接字重置,HttpException表示HTTP失敗好比違背了HTTP協議.一般I/O錯誤是非致命和可恢復,然而HTTP協議錯誤是致命的且沒法自動恢復.請注意HttpClient的實現會從新用java.io.IOException的子類ClientProtocolException拋出HttpException.用戶能夠在同一個catch代碼塊中處理I/O錯誤和協議錯誤.
1.5.1 HTTP傳輸安全
HTTP協議並不適合全部類型的應用。HTTP 是一個面向簡單的請求/響應協議,最初設計來支持靜態或動態生成的內容檢索。HTTP從未有意支持事務操做。好比,HTTP服務器將考慮其合同履行的一部分,若是它成功地接收和處理請求,生成一個響應併發送狀態碼至客戶端。服務器不會試圖回滾事務當客戶端因讀取超時、請求取消或系統故障而接收響應實體失敗。若是客戶端決定重試相同的請求,服務器最終將不可避免地屢次執行相同的事務。在某些狀況下,這可能會致使應用程序數據損壞或不一致的應用程序狀態。
儘管HTTP從未被設計成支持事務處理,它仍然知足做爲關鍵任務應用程序傳輸協議所需知足的條件。
1.5.2 冪等方法
1.5.3 異常自動恢復
默認的HttpClient試圖自動從I/O異常中恢復.默認的自動恢復機制只能處理一小部分異常.
HttpClient不會嘗試從任何邏輯或HTTP協議錯誤中恢復(繼承自HttpException類).
HttpClient將自動重試被認定的冪等方法.
HttpClient將自動重試當HTTP請求仍然在傳輸到目標服務器失敗的方法(好比請求尚未徹底傳輸到服務器).
1.5.4 請求重試處理方法
HttpRequestRetryHandler接口的實現用於自定義異常回復機制.
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler(){ public boolean retryRequest(IOException exception, int executionCouont, HttpContext context){ if(executionCount >= 5){ return false; } if(exception instanceof InterruptedIOException){ return false; } if(exception instanceof UnknownHostException){ return false; } if(exception instanceof ConnecTimeoutException){ return false; } if(exception instanceof SSLException){ return false; } HttpClientContext clientContext = HttpClientContext.adapt(context); HttpRequest request = clientContext.getRequest(); boolean idmpotent - !(request instanceof HttpEntityEnclosingRequest); if(idempotent){ return true; } return false; } }; CloseableHttpClient httpclient = HttpClients.custom().setRetryHandler(myRetryHandler).build();
請注意可使用StandardHttpRequestRetryHandler替代默認配置以便RFC-2616定義的冪等方法能夠安全的自動重試:GET,HEAD,PUT,DELTE,OPTIONS和TRACE.
1.6 終止請求
在某些狀況下HTTP請求可能因爲目標服務器高負荷或者客戶端有太多的請求在使用致使請求在預期的時間範圍內執行失敗.在這種狀況下可能有必要提早終止該請求並解除執行線程對I/O操做的阻塞.經過HttpClient執行的HTTP請求能夠在執行的任何階段調用HttpUriRequest#abort()方法終止.該方法是線程安全的能夠從任何線程調用.當HTTP請求終止執行現場-即便當前阻塞的I/O操做-經過拋出InterruptedIOException保證解除.
1.7 重定向處理
HttpClient自動處理全部類型的重定向,除了那些HTTP規範要求必須用戶介入.POST和PUT請求的see Other(狀態code 303)重定向按HTTP規範的要求轉換成GET請求.能夠自定義重定向策略覆蓋HTTP規範規定的方式.
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy(); CloseableHttpClient httpclient = HttpClients.custom().setRedirectStrategy(redirectStrategy).build();
HttpClient常常須要在執行過程當中重寫請求信息.默認的HTTP/1.0和HTTP/1.1一般使用相對請求URIS.一樣,原始的請求也可能從其餘位置重定向屢次.最總的絕對HTTP位置可以使用原始的請求和上下文得到.工具方法URIUtils#resolve能夠用來解釋絕對URI用於最終的請求.該方法包括重定向請求或原始請求的最後一個片斷標識符.
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpClientContext context = HttpClientContext.create(); HttpGet httpget = new HttpGet(); CloseableHttpResponse response = httpclient.execute(httpget,context); try{ HttpPost target = context.getTargetHost(); List<URI> redirectLocations = context.getRedirectLocations(); URI location = URIUtils.resolve(httpget.getURI(),target,redirectLocations); System.out.println("Final HTTP location: " + location.toASCIIString()); } finally { response.close(); }