微信公众号: 内核小王子
觉得可以的话欢迎关注
场景:公司对外网关对很多外部商户开放,运行多年一直正常,昨天某一个客户调用我们接口的时候频繁报connectiontimeout,异常如下:
该异常来自于httpclient,原因是创建连接超时,也就是tcp进行三次握手的时候失败,或者握手报文没有到达服务端。分析可能有如下原因:
-
1.报文发送太频繁,而客户防火墙性能太差,将报文丢弃
-
2.我们服务端性能过载,accept()方法执行太慢,syn队列或者accept队列满了,导致握手报文被丢弃 ,关于tcp三次握手和发送流程刻参考这篇文章
经过排查后,都不是上面两个原因,目前现象ping包是正常的,执行以下nc命令 ,偶尔会失败,大部分时候成功
while true; do nc -w 2 -zv open.xxx.com 443 ;sleep 1;done
为了排查上面问题,我们先回顾一下这些概念,因为我发现很多人并不清楚短连接加上keepalive和长连接的区别。
长连接: tcp三次握手后,操作系统会通过发送tcp心跳包来保证连接不会因为空闲时间限制被回收,基于长连接客户端可以先发送一个请求,不用等服务端返回立即在发送一个请求
~~~~~~~~~~~~~~~~~~~~~
短连接: tcp三次握手后,请求一次等到收到回复后就会进行四次挥手断开连接。
~~~~~~~~~~~~~~~~~~~~~
http : 目前http都是短连接,并且在1.1版本中采用了keep-alive机制进行重用。短连接加上keep-alive可以让连接保持活性,但是她和长连接的区别是长连接可以不用等第一个请求的回复收到就可以发送第二个请求,因为tcp是一个流,而短连接虽然通过keep-alive保持连接的活性,但他必须等到第一个请求的回复收到后,才能发送第二个请求,一般会用一个连接池进行复用。浏览器请求一个网页,最开始是通过一个连接进行请求,在收到报文回复后解析发现要加载更多的资源,例如图片和js等资源,会在开多个连接,并发的请求服务端,而这些连接会通过keep-alive保持活性放到一个连接池重用,keep-alive机制是在服务端实现的,例如在tomcat的server.xml里面配置,默认一般为1分钟.
<Connector port="8080" maxThread="50" minSpareThreads="25" maxSpareThread="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" maxKeepAliveRequest=100 keepAliveTimeout=60000/>
keepalive : 刚刚说到http的keepalive是在服务端实现的,并且是针对短连接的,有了keepalive的短连接我们一般称为持久连接,而tcp的长连接也需要keepalive机制,客户端和服务端会周期的发送探活报文,这个我们可以通过wireshake进行抓包获取到,有了长连接我们就可以针对这个连接发送请求,并且可以不用等服务端返回后在发送后续请求,为什么http没有用长连接,一般请求一个网页后用户会花时间进行浏览,没有必要用长连接长时间占有资源,但是加载网页的时候为了加快速度,浏览器进行了并发,会同时创建多个连接进行请求,为了防止创建多个连接导致服务器压力太大,所以浏览器限制了同一个域名同时请求的连接数,所以服务端想要加快客户端访问速度可以将资源放到不同的域名来规避浏览器的限制,http没有采用长连接的另一个原因是在http1.1以及之前的版本,一个请求报文和回复报文并不能匹配,并没有定义一个序列号让response和request对应,这样当同时发送多个request之后,客户端在收到response之后不知道和那个request对应,而在http2.0中有定义,所以目前的浏览器都是创建多个连接进行并发,但是单个连接内部必须等上一个请求回复后才能发送下一个请求。
而我们经常会在应用层也会实现一层keepalive探活,例如netty里面IdleSateHandler,很多rpc框架例如dubbo也会单独在应用层实现一层探活机制,有两个原因,一个是应用层可以基于此做一些高可用相关的检测,另外由操作系统发出的tcp探活包仅仅针对网络连接,过于底层,不够灵活,并且即使应用层没有资源处理网络请求底层仍然会对探活包进行响应,而基于应用层就更加灵活,例如服务端可以主动管理连接,客户端也可以主动检测服务端是否可用。
好了会到正题
最终我们发现商户是三台服务器一起请求的,而三台服务器应该是经过nat后是同一个ip,那么很可能是触发了tcp中的一个时间戳的限制,也就是如果同一个ip的请求会记录其时间戳并进行比较,下次发送握手报文的时候,如果时间戳比上一次请求时间小,那么会将该握手报文丢弃,如果同一个ip是同一个机器一般不会有问题,然而三台机器相同ip但是时间戳可能不相同,如果在大批量发送请求的时候很可能会触发该规则。我们询问了下运维的千夜,他确实是在两会期间排查网络问题的时候加了如下参数,开启了回收机制:
net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1
而系统默认
net.ipv4.tcp_timestamps = 1 表示开启时间戳校验
千夜将配置还原后,执行sysctl -p 后没有在报超时异常。
以上所述就是小编给大家介绍的《记录一次http网络超时的排查过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Flink Checkpoint超时问题常见排查思路
- 携程一次 Dubbo 连接超时问题的排查
- redigo设置超时时间
- 深入理解 JDBC 的超时
- [golang]一定要设置超时
- 超时与重试机制(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rust编程之道
张汉东 / 电子工业出版社 / 2019-1 / 128
Rust 是一门利用现代化的类型系统,有机地融合了内存管理、所有权语义和混合编程范式的编程语言。它不仅能科学地保证程序的正确性,还能保证内存安全和线程安全。同时,还有能与C/C++语言媲美的性能,以及能和动态语言媲美的开发效率。 《Rust编程之道》并非对语法内容进行简单罗列讲解,而是从四个维度深入全面且通透地介绍了Rust 语言。从设计哲学出发,探索Rust 语言的内在一致性;从源码分析入......一起来看看 《Rust编程之道》 这本书的介绍吧!