httpclient超時重試記錄

背景

線上服務依賴第三方服務,訪問量較大的狀況下接口RT響應時間很長。因爲每一個超時參數設置爲5s。java

實例

PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
// 鏈接池最大的鏈接數
connManager.setMaxTotal(6000);
// 單個路由的最大鏈接數,例如:www.baidu.com,圖片最大高峯併發量520
connManager.setDefaultMaxPerRoute(500);

RequestConfig.Builder custom = RequestConfig.custom();
// 鏈接超時時間 創建鏈接時間 此前設置3000
custom.setConnectTimeout(800);
// 從connect Manager獲取Connection 超時時間(鏈接池獲取鏈接超時時間)
custom.setConnectionRequestTimeout(500);
// (獲取response的返回時間)讀取返回時間 此前設置40000,官方建議40s
custom.setSocketTimeout(1000);
RequestConfig config = custom.build();

// DefaultHttpRequestRetryHandler 重試策略(可自定義)
httpClient = HttpClients.custom().setRetryHandler(new DefaultHttpRequestRetryHandler(1, true)).setConnectionManager(connManager)
                .setDefaultRequestConfig(config).build();
複製代碼

構造httpClient

經過debug,能夠知道build方法執行之後,獲取到InternalHttpClient對象,若是沒有指定執行鏈,就是用RetryExec執行器,默認的重試策略是DefaultHttpRequestRetryHandlerbash

public CloseableHttpClient build() {
    ... 省略部分代碼
    // Add request retry executor, if not disabled
    if (!automaticRetriesDisabled) {
        HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
        if (retryHandlerCopy == null) {
            retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
        }
        execChain = new RetryExec(execChain, retryHandlerCopy);
    }
    ... 省略部分代碼
}
複製代碼

超時參數

超時設置的三個維度併發

  • 鏈接超時 setConnectTimeout
  • 讀取超時 setSocketTimeout
  • 從鏈接池獲取鏈接超時 setConnectionRequestTimeout

重試策略對業務的影響

默認有重試策略,或者能夠手動更改或者從新定義重試策略,可是一些狀況下,是能夠重試,一些狀況下重試是失效的。socket

重試分析

對於咱們的場景應用中的get與post,能夠總結爲:ide

只有發生IOExecetion時纔會發生重試 InterruptedIOException、UnknownHostException、ConnectException、SSLException,發生這4中異常不重試 get方法能夠重試3次,post方法在socket對應的輸出流沒有被write並flush成功時能夠重試3次。post

不重試的異常:ui

  • InterruptedIOException,線程中斷異常
  • UnknownHostException,找不到對應host
  • ConnectException,找到了host,鏈接創建失敗
  • SSLException,https認證異常

DefaultHttpRequestRetryHandler 重試策略this

@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;
    } else {
        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;
    }

    // 根據上下文判斷請求是否發送成功了,或者根據狀態爲是否永遠能夠重複發送(默認的是否) requestSentRetryEnabled 參數能夠在構建httpCLient對象設置重試策略指定此屬性
    // isRequestSent 是在HttpRequestExecutor中doSendRequest方法中flush成功設置context.setAttribute("http.request_sent", Boolean.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; } // 幾種類型的異常不重試,同時判斷是否與異常類型的子類,是子類的話,也不重試 複製代碼

另外,兩種超時,鏈接超時與讀超時: java.net.SocketTimeoutException: Read timed out java.net.SocketTimeoutException: connect timed out 這兩種超時都是SocketTimeoutException,繼承自InterruptedIOException,屬於上面的第1種線程中斷異常,不會進行重試。spa

哪些狀況下會進行重試

post請求在輸出流進行write與flush的時候,會發生哪些除了InterruptedIOException、UnknownHostException、ConnectException、SSLException之外的IOExecetion。.net

可能出問題一步在於HttpClientConnection.flush()的一步,跟進去能夠得知其操做的對象是一個SocketOutputStream,而這個類的flush是空實現,因此只須要看wirte方法便可。

重試requestSentRetryEnabled http.request_sent參數

//根據上下文判斷請求是否發送成功了,或者根據狀態爲是否永遠能夠重複發送(默認的是否)
if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) {
    return true;
}
複製代碼

禁止重試

httpclient默認提供了重試策略,對於一些場景下,咱們能夠手動關閉重試策略。HttpClientBuilder中,其build()方法中之因此選擇了RetryExec執行器是有前置條件的,即沒有手動禁止。

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

參考

blog.csdn.net/gaohe7091/a…

相關文章
相關標籤/搜索