HttpClient 重试机制的源码解析 (httpclient 超时不重试问题解析) - Go语言中文社区

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


 

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


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

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

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

 

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

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

   

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

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

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

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

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

 

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

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

可以看到只有当请求幂等 和 请求未被完全发送时才会重试,若抛出 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 则代表此异常会触发重试

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Royal_lr/article/details/82148885
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 20:58:19
  • 阅读 ( 565 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢