HTTP调用:超时、重试、并发学习心得

问题描述:

项目场景:调用第三方服务接口,偶发连接超时,记录总结下问题处理过程
在这里插入图片描述


排查方式:

  1. 使用wireshark抓包工具,运行测试代码,出现连接超时之后把报错时间点附近的数据包记录导出

  2. windows系统使用WinMTR测一下路由过程中的丢包率

  3. 查看测试代码中的设置的超时时间,这里我设置的是5秒

HttpConnectionManagerParams params = client.getHttpConnectionManager().getParams();
params.setConnectionTimeout(5000);// 连接超时5秒
params.setSoTimeout(5000);// 等待结果超时5秒

原因分析:

根据测试结果,丢包率比较高,报错的请求服务端没有收到,这个主要是我们到第三方服务器的网络问题导致的。


解决方案:

在我们服务器附近部署了一台新的服务器,测试丢包率。


HTTP调用超时时间及读取时间学习心得

参考资料:
硬核干货:HTTP超时、重复请求必见坑点及解决方案
HTTP调用:你考虑到超时、重试、并发了吗?
网络异常,读取数据超时怎么回事
连接超时和读取超时分别是什么

什么是连接超时和读取超时?

HttpClient连接时间

是HttpClient发送请求的地方开始到连接上目标url主机地址的时间,

理论上是距离越短越快,线路越通畅越快,但是由于路由复杂交错,往往连接上的时间都不固定,运气不好连不上,HttpClient的默认连接时间,默认可能是1分钟,假如超过1分钟 过一会继续尝试连接,这样会有一个问题 假如遇到一个url老是连不上,会影响其他线程的线程进去,所以我们有必要进行特殊设置,比如设置10秒钟 假如10秒钟没有连接上 我们就报错,这样我们就可以进行业务上的处理,比如我们业务上控制 过会再连接试试看。并且这个特殊url写到log4j日志里去。方便管理员查看。

HttpClient读取时间

所谓读取的时间 是HttpClient已经连接到了目标服务器,然后进行内容数据的获取,一般情况 读取数据都是很快速的,但是假如读取的数据量大,或者是目标服务器本身的问题(比如读取数据库慢,并发量大等…)也会影响读取时间。我们还是需要来特殊设置下,比如设置10秒钟 假如10秒钟还没读取完,就报错,同上,我们可以业务上处理。

为什么要设置它们?

超时,无法避免的痛

HTTP调用即通过HTTP协议执行一次网络请求。既然是网络请求,就有超时的可能性(可能你的网卡,也可能服务器所处网络卡),因此在开发中需要注意:

  • 框架设置的默认超时时间是否合理
    1. 过短,请求还未处理完成,你就急不可待了!
    2. 过长,请求早已超出正常响应时间而挂了
  • 考虑网络不稳定性,超时后可以通过定时任务请求重试注意考虑服务端接口幂等性设计,即是否允许重试
  • 考虑框架是否会像浏览器那样限制并发连接数,以免在高并发下,HTTP调用的并发数成为瓶颈

HTTP调用框架技术选型

  • Spring Cloud全家桶使用Feign进行声明式的服务调用。
  • 只使用Spring BootHTTP客户端Apache HttpClient进行服务调用。
连接超时配置 && 读取超时参数

虽然应用层是HTTP协议,但网络层始终是TCP/IP协议。TCP/IP是面向连接的协议,在传输数据之前需要建立连接。所以网络框架都会提供如下超时参数:
在这里插入图片描述

  • 连接超时参数ConnectTimeout可自定义配置的建立连接最长等待时间。
  • 读取超时参数ReadTimeout控制从Socket上读取数据的最长等待时间。

常见踩坑点

1. 连接超时配置过长

比如60s。TCP三次握手正常建立连接所需时间很短,在ms级最多到s级,不可能需要十几、几十秒,多半是网络或防火墙配置问题。这时如果几秒还连不上,那么可能永远也连不上。所以设置特别长的连接超时无意义,1~5秒即可。如果是纯内网调用,还可以设更短,在下游服务无法连接时,快速失败

2. 无脑排查连接超时问题

服务一般会有多个节点,若别的客户端通过负载均衡连接服务端,那么客户端和服务端会直接建立连接,此时出现连接超时大概率是服务端问题而若服务端通过Nginx反向代理来负载均衡,客户端连接的其实是Nginx,而非服务端,此时出现连接超时应排查Nginx

读取超时参数和读取超时“坑点”

只要读取超时,服务端程序的正常执行就一定中断了?

案例:
client接口内部通过HttpClient调用服务端接口server,客户端读取超时2秒,服务端接口执行耗时5秒。在这里插入图片描述
调用client接口后,查看日志:

客户端2s后出现SocketTimeoutException,即读取超时
在这里插入图片描述
服务端却泰然地在3s后执行完成
在这里插入图片描述
Tomcat Web服务器是把服务端请求提交到线程池处理,只要服务端收到请求,网络层面的超时和断开便不会影响服务端的执行。因此,出现读取超时不能随意假设服务端的处理情况,需要根据业务状态考虑如何进行后续处理。

读取超时只是Socket网络层面概念,是数据传输的最长耗时,故将其配置很短,比如100ms。

发生读取超时,网络层面无法区分如下原因:

  1. 服务端没有把数据返回给客户端
  2. 数据在网络上耗时较久或丢包

但TCP是连接建立完成后才传输数据,对于网络情况不是特差的服务调用,可认为:

  • 连接超时网络问题或服务不在线
  • 读取超时服务处理超时。读取超时意味着向Socket写入数据后,我们等到Socket返回数据的超时时间,其中包含的时间或者说绝大部分时间,是服务端处理业务逻辑的时间

超时时间越长,任务接口成功率越高,便将读取超时参数配置过长

HTTP请求一般需要获得结果,属同步调用。若超时时间很长,在等待 Server 返回数据同时,Client 线程(通常为 Tomcat 线程)也在等待,当下游服务出现大量超时,程序可能也会受到拖累创建大量线程,最终崩溃。

  • 对定时任务或异步任务,读取超时配置较长问题不大
  • 但面向用户响应的请求或是微服务平台的同步接口调用,并发量一般较大,应该设置一个较短的读取超时时间,以防止被下游服务拖慢,通常不会设置读取超时超过30s。

评论可能会有人问了,若把读取超时设为2s,而服务端接口需3s,不就永远拿不到执行结果?的确,因此设置读取超时要结合实际情况:

过长可能会让下游抖动影响到自己

网络抖动是指网络发生拥塞的情况下,排队产生的延迟会影响端到端的延迟,并导致通过同一连接传输的分组延迟各不相同,而抖动就是用来描述这样一延迟变化的程度,一般网络抖动值指的是网络通信中延迟最大值与最小值之差,网络抖动值越小说明网络质量越稳定。

过短又可能影响成功率。甚至,有些时候我们还要根据下游服务的SLA,为不同的服务端接口设置不同的客户端读取超时。

最佳实践

连接超时代表建立TCP连接的时间,读取超时代表了等待远端返回数据的时间,也包括远端程序处理的时间。
在解决连接超时问题时,我们要搞清楚连的是谁;在遇到读取超时问题的时候,我们要综合考虑下游服务的服务标准和自己的服务标准,设置合适的读取超时时间。
此外,在使用诸如Spring Cloud Feign等框架时务必确认,连接和读取超时参数的配置是否正确生效。

Feign&&Ribbon
1. 如何配置超时

为Feign配置超时参数的难点在于,Feign自身有两个超时参数,它使用的负载均衡组件Ribbon本身还有相关配置。这些配置的优先级是啥呢?

2.案例
  • 测试服务端超时,假设服务端接口,只休眠10min
    在这里插入图片描述
  • Feign调用该接口:

在这里插入图片描述

  • 通过Feign Client进行接口调用
    在这里插入图片描述

在配置文件仅指定服务端地址的情况下:

clientsdk.ribbon.listOfServers=localhost:45678得到如下输出:

[21:46:24.222][http-nio-45678-exec-4][WARN ][o.g.t.c.h.f.FeignAndRibbonController:26 ] - 执行耗时:222ms 错误:Connect to localhost:45679 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused) executing POST http://clientsdk/feignandribbon/serverFeign默认读取超时是1秒,如此短的读取超时算是“坑”。

  • 分析源码

    在这里插入图片描述
    在这里插入图片描述
    自定义配置Feign客户端的两个全局超时时间

可以设置如下参数:

feign.client.config.default.readTimeout=3000feign.client.config.default.connectTimeout=3000修改配置后重试,得到如下日志:

[http-nio-45678-exec-3][WARN ][o.g.t.c.h.f.FeignAndRibbonController :26 ] - 执行耗时:3006ms 错误:Read timed out executing POST

http://clientsdk/feignandribbon/server3秒读取超时生效。注意:这里有一个大坑,如果希望只修改读取超时,可能会只配置这么一行:
feign.client.config.default.readTimeout=3000测试会发现,这样配置无法生效。

要配置Feign读取超时,必须同时配置连接超时

查看FeignClientFactoryBean源码

只有同时设置ConnectTimeout、ReadTimeout,Request.Options才会被覆盖
在这里插入图片描述
想针对单独的Feign Client设置超时时间,可以把default替换为Client的name:

feign.client.config.default.readTimeout=3000feign.client.config.default.connectTimeout=3000feign.client.config.clientsdk.readTimeout=2000feign.client.config.clientsdk.connectTimeout=2000单独的超时可覆盖全局超时

[http-nio-45678-exec-3][WARN ][o.g.t.c.h.f.FeignAndRibbonController :26 ] - 执行耗时:2006ms 错误:Read timed out executing POST http://clientsdk/feignandribbon/server除了可以配置Feign,也可配置Ribbon组件的参数以修改两个超时时间

参数首字母要大写,和Feign的配置不同。

ribbon.ReadTimeout=4000ribbon.ConnectTimeout=4000可以通过日志证明参数生效:

[http-nio-45678-exec-3][WARN ][o.g.t.c.h.f.FeignAndRibbonController :26 ] - 执行耗时:4003ms 错误:Read timed out executing POST http://clientsdk/feignandribbon/server同时配置Feign和Ribbon的参数

谁会生效?

clientsdk.ribbon.listOfServers=localhost:45678feign.client.config.default.readTimeout=3000feign.client.config.default.connectTimeout=3000ribbon.ReadTimeout=4000ribbon.ConnectTimeout=4000最终生效的是Feign的超时:
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值