记录一次http网络超时的排查过程

微信公众号:内核小王子 以为能够的话欢迎关注浏览器

场景:公司对外网关对不少外部商户开放,运行多年一直正常,昨天某一个客户调用咱们接口的时候频繁报connectiontimeout,异常以下:tomcat

该异常来自于httpclient,缘由是建立链接超时,也就是tcp进行三次握手的时候失败,或者握手报文没有到达服务端。分析可能有以下缘由:bash

  • 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探活包仅仅针对网络链接,过于底层,不够灵活,而且即便应用层没有资源处理网络请求底层仍然会对探活包进行响应,而基于应用层就更加灵活,例如服务端能够主动管理链接,客户端也能够主动检测服务端是否可用。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 后没有在报超时异常。

历史文章:

JAVA和操做系统交互细节

经过MySQL存储原理来分析排序和锁

网络内核之TCP是如何发送和接收消息的

相关文章
相关标签/搜索