共计 3390 个字符,预计需要花费 9 分钟才能阅读完成。
本文基于 HttpClient
4.5.13
应用 http 申请内部服务时,因为网络或者服务自身的不稳定性,常常须要重试。重试当然能够通过手撸代码实现,但更好的形式是通过现有的机制去实现。HttpClient
中反对两种重试:
- 异样重试。
- 服务不可用重试。
异样重试
HttpClient
执行时会抛出两种异样:
java.io.IOException
ClientProtocolException
java.io.IOException
被认为是非致命性且可复原的,而 ClientProtocolException
被认为是致命性的,不可复原。
解决的时候要留神,ClientProtocolException
是 java.io.IOException
的子类。
如果是这样创立 HttpClient
的
CloseableHttpClient httpClient = HttpClients.custom().build();
异样重试是默认开启的,具体代码能够参考 HttpClientBuilder.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);
}
automaticRetriesDisabled
是一个 boolean
类型的变量,默认为 false
,所以条件默认是成立的,如果没有设置 HttpRequestRetryHandler
就会用一个默认的。
DefaultHttpRequestRetryHandler
次要有三个成员变量
retryCount
重试次数requestSentRetryEnabled
是否能够在申请胜利收回后重试,这里的胜利是指发送胜利,并不指申请胜利。nonRetriableClasses
不重试的异样类汇合,如果异样为汇合中指定的异样时,不会重试。
默认的实例变量设置如下
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
来实现本人的需要。
另外还反对设置重试申请的间隔时间。
看到了这里肯定是真爱了,关注微信公众号【码农张思壮】第一工夫获取更新。