本文基于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
来实现本身的需求。
另外还支持设置重试请求的间隔时间。
看到了这里必定是真爱了,关注微信公众号【码农张思壮】第一时间获取更新。