線上服務依賴第三方服務,訪問量較大的狀況下接口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();
複製代碼
經過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);
}
... 省略部分代碼
}
複製代碼
超時設置的三個維度併發
默認有重試策略,或者能夠手動更改或者從新定義重試策略,可是一些狀況下,是能夠重試,一些狀況下重試是失效的。socket
對於咱們的場景應用中的get與post,能夠總結爲:ide
只有發生IOExecetion時纔會發生重試 InterruptedIOException、UnknownHostException、ConnectException、SSLException,發生這4中異常不重試 get方法能夠重試3次,post方法在socket對應的輸出流沒有被write並flush成功時能夠重試3次。post
不重試的異常:ui
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方法便可。
//根據上下文判斷請求是否發送成功了,或者根據狀態爲是否永遠能夠重複發送(默認的是否)
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);
}
複製代碼