HttpClient重试策略导致的SocketTimeoutException异常

前言

有业务部门反馈,在使用SOA框架进行远程调用的时候,出现SocketTimeoutException异常,并且发现是在HTTP status =429的时候才会造成这种情况

背景介绍

  1. 我们的SOA框架有一种场景,为了兼容老的业务能介入服务化治理,使用了一种称为”泛华的方式“,让业务能像调用本地接口一样,调用下游的HTTP服务
  2. 业务服务端:第一次访问接口时候,返回的是200,再次重复调用,会返回HTTP status =429 (具体含义参考HTTP规范)

排查流程

  1. 业务首先演示了使用我们的SOA框架的时候,第一次调用f返回200的时候,是正常的耗时ms级别,第二次时候耗时11S多, 且抛出SocketTimeoutException异常
###|2022-10-28 18:27:33.610|ERROR|-|b431ac67-a4ad-4ff3-834b-34a1f9030338|http-nio-8080-exec-5|SoaRequestAspect--->soa call error 
java.net.SocketTimeoutException: 10000 MILLISECONDS
	at org.apache.hc.core5.io.SocketTimeoutExceptionFactory.create(SocketTimeoutExceptionFactory.java:50)
	at org.apache.hc.core5.http.impl.nio.AbstractHttp1StreamDuplexer.onTimeout(AbstractHttp1StreamDuplexer.java:398)
	at org.apache.hc.core5.http.impl.nio.AbstractHttp1IOEventHandler.timeout(AbstractHttp1IOEventHandler.java:82)
	at org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler.timeout(ClientHttp1IOEventHandler.java:39)
	at org.apache.hc.core5.reactor.InternalDataChannel.onTimeout(InternalDataChannel.java:158)
	at org.apache.hc.core5.reactor.InternalChannel.checkTimeout(InternalChannel.java:67)
	at org.apache.hc.core5.reactor.SingleCoreIOReactor.checkTimeout(SingleCoreIOReactor.java:241)
	at org.apache.hc.core5.reactor.SingleCoreIOReactor.validateActiveChannels(SingleCoreIOReactor.java:168)
	at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:130)
	at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:85)
	at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
	at java.lang.Thread.run(Thread.java:748)
###|2022-10-28 18:27:33.615|WARN|-|b431ac67-a4ad-4ff3-834b-34a1f9030338|http-nio-8080-exec-5|BusinessParamException--->业务错误,ret:null, code:svc..null, msg:sever logic err
  1. 使用postman调用的时候,第一次调用(返回200)和第二次调用(返回429)都很快且不会超时

  2. 和业务方一起检查服务端日志,发现不管是SOA代码还是postman调用,均在几ms就返回结果

  3. 在本地使用Wireshark查看TCP连接情况
    在这里插入图片描述

  4. 分析上面TCP连接情况,发现在No.858 行返回429结果后,在No.886 又有一次PUSH请求,查看发现是有一次http请求
    在这里插入图片描述

  5. No.418 第一次发起请求,No.427 返回HTTP status = 200,正常

  6. No.841 第二次发起请求,No.858 返回HTTP status = 429(再次调用是通过重启客户端方式)

  7. No.886 客户端发起重试,查看报文发现是只重发了请求头,请求体Body丢失了

  8. 服务端一直在等待客户端发送请求体,导致超时

  9. 通过上诉日志链接,查看服务端只返回了 1 次HTTP status = 429,No.886 客户端发起重试的没有返回日志

  10. 这个是HttpClient的一个Bug,目前已经在5.1.3版本修复,具体可查看 ISSUES HTTPCLIENT-2194 或者 Version 5.1.3 Release Notes

原因:
HttpClient5 重试时未正常发送请求体,导致的服务端等待超时

附录

  1. 正常的HttpClient重试流程抓包流程
    在这里插入图片描述

  2. HttpClient重试策略代码

客户端使用的重试策略是默认的重试策略,具体在代码 org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy
4.X版本默认重试3次,5.X版本默认重试1次

创建HttpClient


CloseableHttpAsyncClient asyncClient = builder.build();

在build方法中有HttpRequestRetryStrategy的设置,默认是实现是DefaultHttpRequestRetryStrategy

// Add request retry executor, if not disabled
        if (!automaticRetriesDisabled) {
            HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
            if (retryStrategyCopy == null) {
                retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
            }
            execChainDefinition.addFirst(
                    new AsyncHttpRequestRetryExec(retryStrategyCopy),
                    ChainElement.RETRY.name());
        }

查看DefaultHttpRequestRetryStrategy代码可知

在以下两种情况均会重试

  1. 发生除以下的异常:InterruptedIOException.class,UnknownHostException.class,ConnectException.class,ConnectionClosedException.class,NoRouteToHostException.class,SSLException.class
  2. 返回HTTP status=429 或 503 时

1、在5.X版本中,默认重试一次,在4.X版本是重试3次
2、除了以下几种异常的时候发生异常会重试

org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy#retryRequest(org.apache.hc.core5.http.HttpRequest, java.io.IOException, int, org.apache.hc.core5.http.protocol.HttpContext)

InterruptedIOException.class,
UnknownHostException.class,
ConnectException.class,
ConnectionClosedException.class,
NoRouteToHostException.class,
SSLException.class)
3、 Response的http statuis在下面两种情况也会重试

org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy#retryRequest(org.apache.hc.core5.http.HttpResponse, int, org.apache.hc.core5.http.protocol.HttpContext)

HttpStatus.SC_TOO_MANY_REQUESTS,
HttpStatus.SC_SERVICE_UNAVAILABLE)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值