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 则代表此异常会触发重试

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
HttpClient 可以通过设置重试策略来实现重试操作。在 HttpClient 中,有两种方式可以设置重试策略: 1. 使用 HttpRequestRetryHandler 接口实现自定义重试策略 你可以自定义一个实现了 HttpRequestRetryHandler 接口的类,并在 HttpClient 中设置该重试处理器。例如,以下代码展示了一个最大重试次数为 3 的重试处理器实现: ``` HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { if (executionCount >= 3) { // Do not retry if over max retry count return false; } if (exception instanceof InterruptedIOException) { // Timeout return false; } if (exception instanceof UnknownHostException) { // Unknown host return false; } if (exception instanceof ConnectTimeoutException) { // Connection refused return true; } if (exception instanceof SSLException) { // SSL handshake exception return false; } HttpClientContext clientContext = HttpClientContext.adapt(context); HttpRequest request = clientContext.getRequest(); boolean idempotent = !(request instanceof HttpEntityEnclosingRequest); if (idempotent) { // Retry if the request is considered idempotent return true; } return false; } }; CloseableHttpClient httpClient = HttpClients.custom() .setRetryHandler(myRetryHandler) .build(); ``` 以上代码中,我们将重试次数设置为 3,如果超过次数则不再尝试。HttpRequestRetryHandler 接口中的 retryRequest 方法定义了什么情况下需要重试,可以根据需要进行自定义。 2. 使用 HttpClientBuilder 设置重试策略 在 HttpClient 中,HttpClientBuilder 提供了多个重试策略,可以通过 setRetryHandler 方法来设置。例如,以下代码展示了一个最大重试次数为 3 的重试策略: ``` CloseableHttpClient httpClient = HttpClients.custom() .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) .build(); ``` 以上代码中,我们使用了 DefaultHttpRequestRetryHandler 类来实现重试操作,设置了最大重试次数为 3,以及当遇到 IOException 时是否重试。DefaultHttpRequestRetryHandler 类还有其他构造方法可以根据需要进行选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值