1. 程式人生 > >《HttpClient官方文件》1.5 異常處理

《HttpClient官方文件》1.5 異常處理

1.5. 異常處理

HTTP協議處理器會丟擲兩種型別的異常: 一種是I/O失敗的情況下產生的java.io.IOException,比如套接字超時或重置。 另一種是傳送HTTP訊號失敗情況下的HttpException,比如違反HTTP協議。 通常情況,我們認為I/O錯誤是非致命且可恢復的錯誤,而HTTP協議錯誤則是致命且不能自動恢復的。 請注意,HttpClient將HttpException重新封裝成一個IOException的子類ClientProtocolException,這樣使用者就可以在一個catch程式碼塊中同時處理I/O錯誤和違反協議的錯誤。

1.5.1. HTTP 傳輸安全

很重要的一點是,HTTP協議並不適用於所有型別的應用程式。 HTTP是一個簡單的面相request/response的協議,它的設計初衷是為了支援靜態或動態地生成內容檢索。 它從未打算支援事務操作,比如,當HTTP伺服器成功地接收並處理了客戶端傳送的請求,生成相應併發送回狀態碼,那麼它會就認為完成了約定的內容。 如果由於讀超時,請求取消或者系統崩潰等原因導致客戶端無法成功接收到響應,那麼伺服器不會試圖去回滾事務。如果客戶端決定重試相同的請求,伺服器最終將不可避免地多次執行相同的事務。 在某些情況下,這有可能導致應用程式的資料汙染或狀態不一致。 儘管HTTP從未被設計成支援事務處理,但是它仍然可以作為一種在特定條件下滿足特殊任務的傳輸協議。 為了保證HTTP傳輸層的安全性,系統必須保證應用層中的HTTP方法是冪等的。

1.5.2. 冪等方法

HTTP / 1.1規範定義了一個冪等方法[N > 0 的多次請求同單次請求產生的效果一樣(除了錯誤或過期問題),這樣的方法具有“冪等性”的性質]。換句話說,應用程式應該確保處理多次執行含義相同的方法的情況。 比如,應用程式可以通過提供一個獨特的transaction id或者通過其他方式避免執行相同的邏輯操作來實現。 請注意,這個問題並不侷限於HttpClient。 基於瀏覽器的應用程式正是受同樣涉及非冪等性的HTTP方法影響。

1.5.3. 異常自動恢復

預設情況下HttpClient嘗試從I/O exception中自動恢復。預設的自動恢復機制只侷限於少數已知的安全的異常。

  • HttpClient不會試圖從任何邏輯或HTTP協議錯誤(衍生自HttpException類)中恢復。
  • HttpClient將自動重試那些被認為是等冪的方法。
  • HttpClient將自動重試那些因傳輸異常而失敗但是HTTP請求仍然會被髮送到目標伺服器的方法。(即請求還沒有完全被髮送到伺服器端)

1.5.4. 請求重試處理

為了使自定義異常恢復機制有效,實現了HttpRequestRetryHandler介面。

HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(
IOException exception,
int executionCount,
HttpContext context) {
if (executionCount >= 5) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof InterruptedIOException) {
// Timeout
return false;
}
if (exception instanceof UnknownHostException) {
// Unknown host
return false;
}
if (exception instanceof ConnectTimeoutException) {
// Connection refused
return false;
}
if (exception instanceof SSLException) {
// SSL handshake exception
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRetryHandler(myRetryHandler)
.build();

請注意,為了處理安全自動的重試RFC-2616協議中定義的請求方法GET,HEAD,PUT,DELETE,OPTIONS和TRACE,可以用StandardHttpRequestRetryHandler代替使用預設的。