HttpClient的兩種重試機制

本文基於HttpClient 4.5.13java

使用 http 請求外部服務時,因爲網絡或者服務自己的不穩定性,常常須要重試。重試固然能夠經過手擼代碼實現,但更好的方式是經過現有的機制去實現。HttpClient 中支持兩種重試:apache

  1. 異常重試。
  2. 服務不可用重試。

異常重試

HttpClient 執行時會拋出兩種異常:微信

  • java.io.IOException
  • ClientProtocolException

java.io.IOException 被認爲是非致命性且可恢復的,而 ClientProtocolException 被認爲是致命性的,不可恢復。網絡

處理的時候要注意,ClientProtocolExceptionjava.io.IOException 的子類。ide

若是是這樣建立 HttpClientpost

CloseableHttpClient httpClient = HttpClients.custom().build();

異常重試是默認開啓的,具體代碼能夠參考 HttpClientBuilder.build() 方法,下面是相關的代碼ui

// Add request retry executor, if not disabled
if (!automaticRetriesDisabled) {
    HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
    if (retryHandlerCopy == null) {
        retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
    }
    execChain = new RetryExec(execChain, retryHandlerCopy);
}

automaticRetriesDisabled 是一個 boolean 類型的變量,默認爲 false ,因此條件默認是成立的,若是沒有設置 HttpRequestRetryHandler 就會用一個默認的。this

DefaultHttpRequestRetryHandler主要有三個成員變量spa

  • retryCount重試次數
  • requestSentRetryEnabled 是否能夠在請求成功發出後重試,這裏的成功是指發送成功,並不指請求成功。
  • nonRetriableClasses 不重試的異常類集合,若是異常爲集合中指定的異常時,不會重試。

默認的實例變量設置以下code

  • retryCount=3,最多重試3次。
  • requestSentRetryEnabled=false,發送成功的就不會重試了
  • nonRetriableClasses包含了如下四種:

    • InterruptedIOException
    • UnknownHostException
    • ConnectException
    • SSLException

重試的執行邏輯在org.apache.http.impl.execchain.RetryExec,有興趣的能夠去看下。

默認的是否重試邏輯以下

@Override
public boolean retryRequest(final IOException exception, final int executionCount, final HttpContext context) {
    Args.notNull(exception, "Exception parameter");
    Args.notNull(context, "HTTP context");
    if (executionCount > this.retryCount) {
        // Do not retry if over max retry count
        // 超太重試次數不重試
        return false;
    }
    // 若是是忽略的異常不重試
    if (this.nonRetriableClasses.contains(exception.getClass())) {
        return false;
    }
    for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {
        if (rejectException.isInstance(exception)) {
            return false;
        }
    }
    final HttpClientContext clientContext = HttpClientContext.adapt(context);
    final HttpRequest request = clientContext.getRequest();

    if(requestIsAborted(request)){
        return false;
    }
    // 冪等方法能夠重試
    if (handleAsIdempotent(request)) {
        // Retry if the request is considered idempotent
        return true;
    }
    // 若是請求沒有發送或者發送了也重試
    if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {
        // Retry if the request has not been sent fully or
        // if it's OK to retry methods that have been sent
        return true;
    }
    // otherwise do not retry
    return false;
}

這裏要注意下冪等方法,postput 都不是,因此那裏的判斷不會成立,可是若是 requestSentRetryEnabled 設置爲 true,仍是會重發的,那就須要保證被調用的接口再處理 postput 的請求時是冪等的。

有些人可能會遇到問題,好比報了 SocketTimeoutException 的異常,可是沒有重試,這是由於 SocketTimeoutExceptionInterruptedIOException 的子類,默認會被忽略。若是須要重試,能夠自定義一個 HttpRequestRetryHandler ,而後再設置就能夠了。

HttpClients.custom().setRetryHandler(httpRequestRetryHandler).build();

實際上使用的時候繼承 DefaultHttpRequestRetryHandler,而後擴展一些本身的實現就很方便。

若是想禁用調重試也很簡單

HttpClients.custom().disableAutomaticRetries().build();

服務不可用重試

有的時候,請求成功了,可是 http 狀態碼可能不是 2xx,這種狀況也須要重試。HttpClient 中提供了在服務不可用時進行重試的機制。

重試執行的邏輯在 org.apache.http.impl.execchain.ServiceUnavailableRetryExec,有興趣能夠看下。

HttpClient 中提供了默認的策略,可是沒有默認開啓,須要本身設置

DefaultServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new DefaultServiceUnavailableRetryStrategy();
httpClient = HttpClients.custom().setServiceUnavailableRetryStrategy(serviceUnavailableRetryStrategy).build();

重試的邏輯

@Override
public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
    return executionCount <= maxRetries &&
        response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
}

當沒有超太重試次數,且返回碼爲503的時候進行重試,固然也能夠自定義 ServiceUnavailableRetryStrategy 來實現本身的需求。

另外還支持設置重試請求的間隔時間。

看到了這裏必定是真愛了,關注微信公衆號【碼農張思壯】第一時間獲取更新。

image

相關文章
相關標籤/搜索