HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)

 

友情提示 : 超时重试的解决方案在最下面,可直接查看


首先介绍一下我的版本是httpclient 4.3.4,采用的是  PoolingHttpClientConnectionManager 连接池的方式构造 CloseableHttpClient,代码如下:

接下来,执行如下图所示的get 请求:

查看execute 方法 ,一直到下图所示的

 HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第1张图片

doExecute 方法,可以看到多种实现方式,查看构造httpclient的build 构造方法到最后一行 发现 new InternalHttpClient ,然后进入其 doExecute 方法 ,直到下图的这一行

发现会有一个 ClientExecChain execChain 执行链 在执行,继续查看 httpclient的build 方法 ,可以看到如下的3段代码

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第2张图片   HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第3张图片

有多个执行链在进行调用,这里采用的是一个责任链的设计模式,每一个 execChain 链都会调用其上一个的

execute 方法,所以会依次进入 MainClientExec 、ProtocolExec 、RetryExec的 execute方法,首先查看MainClientExec 的execute方法 查看到下面这行代码

持有一个 requestExecutor 会执行,然后 会执行 HttpRequestExecutor execute方法 

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第4张图片

这段代码会执行http 请求获取响应值。现在我们假设请求出现超时,会抛出  IOException 到 ProtocolExec 的 execute 方法中,如下图:

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第5张图片

所以异常继续上抛到  RetryExec 的 execute 方法中,如下图:

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第6张图片

 

会有一个循环,捕获到  IOException 后 由于 execAware.isAborted() 默认返回false, 会进入到  retryHandler.retryRequest 这段方法中. 接下来继续 查看 上图中  httpclient的build 方法 会发现 若重试类  为空,则会默认构造  DefaultHttpRequestRetryHandler ,查看 其构造方法,如下图:

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第7张图片

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第8张图片

默认重试次数为 3,不会重试已经发送的请求。查看 其重试请求方法 

HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析)_第9张图片

可以看到只有当请求幂等 和 请求未被完全发送时才会重试,若抛出 InterruptedIOException 、UnknownHostException、ConnectException 、SSLException 这四种异常,http请求不会被重试的。

上图我们发现 超时异常均继承于 InterruptedIOException ,所以无论是连接超时还是读取超时都不会触发重试机制。

httpClient的超时重试的解决方案

我的解决方法是重新实现一个 重试类 HttpRequestRetryHandler ,代码如下:

HttpRequestRetryHandler requestRetryHandler=new HttpRequestRetryHandler() {
   public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
      if (executionCount > 3) //超过重试次数,就放弃
         return false;
      if (exception instanceof NoHttpResponseException) {//没有响应,重试
         return true;
      }else if (exception instanceof ConnectTimeoutException) {//连接超时,重试
         return true;
      } else if (exception instanceof SocketTimeoutException) {//连接或读取超时,重试
         return true;
      }else if (exception instanceof SSLHandshakeException) {//本地证书异常
         return false;
      } else if (exception instanceof InterruptedIOException) {//被中断
         return false;
      } else if (exception instanceof UnknownHostException) {//找不到服务器
         return false;
      }  else if (exception instanceof SSLException) {//SSL异常
         return false;
      } else {
         LOGGER.error("未记录的请求异常:" + exception.getClass());
      }
      HttpClientContext clientContext = HttpClientContext.adapt(context);
      HttpRequest request = clientContext.getRequest();
      // 如果请求是幂等的,则重试
      if (!(request instanceof HttpEntityEnclosingRequest)) return true;
      return false;
   }
};
httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).setRetryHandler(requestRetryHandler).build();

第一个 executionCount 是代表的重试次数,而  return true 则代表此异常会触发重试

你可能感兴趣的:(http请求,超时不重试)