本文基於HttpClient
4.5.13java
使用 http 請求外部服務時,因爲網絡或者服務自己的不穩定性,常常須要重試。重試固然能夠經過手擼代碼實現,但更好的方式是經過現有的機制去實現。HttpClient
中支持兩種重試:apache
HttpClient
執行時會拋出兩種異常:微信
java.io.IOException
ClientProtocolException
java.io.IOException
被認爲是非致命性且可恢復的,而 ClientProtocolException
被認爲是致命性的,不可恢復。網絡
處理的時候要注意,ClientProtocolException
是 java.io.IOException
的子類。ide
若是是這樣建立 HttpClient
的post
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; }
這裏要注意下冪等方法,post
和 put
都不是,因此那裏的判斷不會成立,可是若是 requestSentRetryEnabled
設置爲 true
,仍是會重發的,那就須要保證被調用的接口再處理 post
和 put
的請求時是冪等的。
有些人可能會遇到問題,好比報了 SocketTimeoutException
的異常,可是沒有重試,這是由於 SocketTimeoutException
是 InterruptedIOException
的子類,默認會被忽略。若是須要重試,能夠自定義一個 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
來實現本身的需求。
另外還支持設置重試請求的間隔時間。
看到了這裏必定是真愛了,關注微信公衆號【碼農張思壯】第一時間獲取更新。