热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

http默认超时时间_记一次网络请求连接超时的事故

http默认超时时间_记一次网络请求连接超时的事故,Go语言社区,Golang程序员人脉社



dc4c0868d89d0fddb27bb1a6f8c67582.gif点击上方蓝色字关注我们~
前言

HTTP请求超时、重试机制、操作系统网络等层面剖析了事故的原因,最终解决业务问题。


这里先抛两个问题


1)你遭遇过由于网络连接或请求超时造成的生产事故吗?


2)你知道操作系统默认的网络连接超时是多少秒?


先思考下,可以将你的答案写在评论区哦。


问题背景

最近同事出现这么一个问题,简单业务场景:


456ed40230b4a87759403d40820c7a7e.png


服务A使用HTTP请求服务B接口m。服务A起了一个定时任务Task:


从db查询数据总共有1200+条,每条记录对应一次请求,循环调用m接口。服务B收到请求会使用TCP连接其它服务器机器,进行命令的交互。注意这里并不是异步并发去请求接口,因为如果异步并发请求,可能就造成服务B的处理线程很快用光,从而造成不会再很好的处理更多请求,甚至会造成大面积请求超时或服务宕机等等问题。


此时定时任务到时间跑起来了,过不了多久,服务A出现向B请求Hand住,最终出现超时。


de7385ea6156c28306047877f6c5f3a8.png


如下超时日志Read timed out:


ab279bf5b9235973ff625788e46a5dd6.png


虽然服务A自身查询DB等服务是正常的,但是服务A和服务B之间的交互也很重要,如果两者之间出现问题,必然会对业务处理或者系统等方面造成影响。


所以到底是为什么,这里涉及了什么问题呢?


问题解决

1、重试机制加快问题出现


此时在服务A上进行排查,通过elk日志发现异常日志,异常日志数量激增。如下截图:


c9397ac4e548e657418ec5def0e529ed.png


异常日志明细:


df269e3d1cc16cce7bea2e31f22ddf07.png


org.apache.http.impl.execchain.RetryExec,由此可知应该跟http重试机制相关。


e8b8002aa2e12d5d808b4c4f9ae14518.png


由RetryExec源码可知,当http执行请求时,如果正常请求则立即返回;否则IOException异常时,则进入重试环节。


这里要注意下,for循环进行重试是死循环的方式,这里的重试次数由实现者控制,如果无需重试,默认则不会进行重试,而是直接抛出异常。


查看RetryHandler的自定义实现源码:


@Component
public class HttpRequestRetryHandlerServer implements HttpRequestRetryHandler {
   protected static final Logger LOG = LoggerFactory.getLogger(HttpRequestRetryHandlerServer.class);
   @Override
   public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
      if (retryCount >= 3) {
         LOG.warn("Maximum tries reached, exception would be thrown to outer block");
         return false;
      }
      if (e instanceof org.apache.http.NoHttpResponseException) {
         LOG.warn("No response from server on {} call", retryCount);
         return true;
      }
      return false;
   }
}

从源码知道,重试次数最多3次,并且只针对这种异常NoHttpResponseException,从命名知道这是HTTP无响应异常(源码注释是:Signals that the target server failed to respond with a valid HTTP response.)。


那么服务A为什么会进入重试流程呢?


由上面的异常知道,可以排除是由于网络连接超时出现的异常,而是正常请求,但是由于可能某种原因,迟迟没有得到正常响应结果。由前面的异常Read timed out知道是出现读超时异常,这里就考虑到可能是跟网络数据传输等参数相关。


查看默认配置:


6b867339db37bdd88b023e3958cfbf88.png


由此可知,6秒是数据传输的最长时间(读超时)。http请求时等待数据结果如果超过6秒,就会中断当前的请求,抛出Read timed out异常。所以基本上就可以知道这个异常的原由了。


2、重试机制加快问题-解决方法


分析当前场景,于是做下调整:


1)由于此场景http请求无需进行重试,则将其关闭:


@Bean
public CloseableHttpClient noRetryHttpClient(HttpClientBuilder clientBuilder) {
    // 重试次数为0,不进行重试
    clientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
    return clientBuilder.build();
}

2)由于此业务请求,服务B可能出现超过6秒的处理时长,则socketTimeout调整为15秒:


# http pool config
http:
  maxTotal: 500
  defaultMaxPerRoute: 100
  connectTimeout: 5000
  connectionRequestTimeout: 3000
  socketTimeout: 15000
  maxIdleTime: 1
  keepAliveTimeOut: 65

3、机器连接超时的锅


de7385ea6156c28306047877f6c5f3a8.png


接下来再排查服务B到底是怎么了?也就是上图的右侧“闪电”。为什么需要这么长的处理时间。


服务A发起Http请求时,服务B接收请求后进行跟服务器进行连接后交互数据。服务B与服务机器通信只要是使用Tcp ssh的方式,也就是会进行网络通信。


经过排查服务B的日志:


8150c35e4b673527195213843dbc8d3b.png


可知,进行连接服务器时出现异常。注意这个连接耗时时长为:63秒左右。并且确认排查目标服务器确实没有正常工作,而是已经停机许久。


由于进行的是Unix平台的网络连接,当前的操作系统是Linux CentOS。那么为什么这个超时时间是63秒,而不是5秒、15秒、60秒等比较规整的数据呢?


此时查看网络连接的代码:


connection.connect();

可知这里并没有指定连接超时等参数,那么应该是使用了操作系统内核的默认参数了。


Linux 系统默认的建立 TCP 连接的超时时间为 127 秒。这个对于客户端一般都是比较长了,更多的业务场景基本不会使用默认值,而是根据业务场景进行设置合理的连接超时时间。那么这个时间是怎么来的?为什么是127?


其实这个时间参数是由:net.ipv4.tcp_syn_retries配置的等级来确定的。


net.ipv4.tcp_syn_retries 的设置,表示应用程序进行connect()系统调用时,在对方不返回SYN + ACK的情况下(也就是超时的情况下),第一次发送之后,内核最多重试几次发送SYN包;并且决定了等待时间。


Linux上的默认值是 net.ipv4.tcp_syn_retries = 6 ,也就是说如果是本机主动发起连接,(即主动开启TCP三次握手中的第一个SYN包),如果一直收不到对方返回SYN + ACK ,那么应用程序最大的超时时间就是127秒。


第 1 次发送 SYN 报文后等待 1s(2 的 0 次幂),如果超时,则重试;


第 2 次发送后等待 2s(2 的 1 次幂),如果超时,则重试;


第 3 次发送后等待 4s(2 的 2 次幂),如果超时,则重试;


第 4 次发送后等待 8s(2 的 3 次幂),如果超时,则重试;


第 5 次发送后等待 16s(2 的 4 次幂),如果超时,则重试;


第 6 次发送后等待 32s(2 的 5 次幂),如果超时,则重试;


第 7 次发送后等待 64s(2 的 6 次幂),如果超时,则超时失败。


接下来查看我们的机器上的tcp syn 参数:


32a40359529eecd822ba065b077166b7.png


而我们的服务器设置的tcp_syn_retries为5,即默认超时为=1+2+4+8+16+32=63秒。刚好与当前问题完美符合,这就是为什么出现63秒超时原由了。


4、那么在Windows平台,又是怎么样的呢?


(本来这部分不准备阐述的,希望读者自行查阅资料,但是还是做个完整的吧。)


因为我是用Windows10作为开发机器的,所以顺便想了解下在Windows平台下,它的超时时间是多少。写了个测试用例,一测,竟然是21秒左右。这又是什么原理??


查阅相关资料:


TcpMaxConnectRetransmissions


Determines how many times TCP retransmits an unanswered request for a new connection. TCP retransmits new connection requests until they are answered, or until this value expires.


TCP/IP adjusts the frequency of retransmissions over time. The delay between the original transmission and the first retransmissions for each interface is determined by the value of TcpInitialRTT (by default, it is 3 seconds). This delay is doubled after each attempt. After the final attempt, TCP/IP waits for an interval equal to double the last delay and then abandons the connection request.


TcpInitialRTT


Determines how quickly TCP retransmits a connection request if it doesn't receive a response to the original request for a new connection.


By default, the retransmission timer is initialized to 3 seconds, and the request (SYN) is sent twice, as specified in the value of TcpMaxConnectRetransmissions.


由资料可知,在Windows平台上是由此参数:TcpMaxConnectRetransmissions和TcpInitialRTT控制,TcpMaxConnectRetransmissions默认值一般为2,TcpInitialRTT默认是3秒。


也就是会进行2次重试,每次是上次的2倍时间,即21秒为:3+3*2+(3*2)*2=3+6+12=21秒。


通过命令查询Windows参数:


netsh interface tcp show global

8d5b5afdb80f3c7c838115d167099cc4.png


这个最大SYN重新传输次数我的公司开发机器是2,但是我的个人机器却为4(那么默认连接超时时长为:3+6+12+24+48=93秒),虽然都是Windows10系统,但是为什么不同这个就不得而知了。


5、机器连接超时的锅-解决方法


服务B网络连接服务器时设置连接超时时间为5秒:


connection.connect(null, 5000, kexTimout);

这样只要超过5秒还没能连接上,就做超时异常处理,及早释放资源,不再阻塞当前处理线程。


6、结果


通过相关的调整优化,重新发布服务验证,最终服务稳定运行,不会再出现异常等情况。


perfect!


总结

1)虽然这次事故造成的罪魁祸首不是服务A的HTTP重试机制,但是也是它加快了问题的出现速度。


所以我们要清楚是否需要重试机制,如果不需要就不要设置,不然非常浪费资源,甚至会压垮服务提供方系统等问题。


2)网络连接一般有TCP和HTTP等,防止超时时间太久影响业务、甚至造成服务宕机等严重问题,一般都要设置合理的超时时间(连接超时时间和数据传输时间等)。


因为操作系统设置的是比较通用的默认参数,并不会考虑具体的业务场景。


网络数据传输时间:具体的业务场景是非常不同的,比如默认6秒的数据传输时间,在实际的场景下并不一定合理,此时需要根据实际进行调整,比如我这边的情况是调整为15秒。


网络连接超时:比如Windows平台网络连接超时默认一般是21秒,Linux(Centos)有默认阶梯超时机制,默认127秒,而在我这台机器上则是63秒。


3)学习操作系统超时机制。比如Linux或Windows,在连接超时时可以在前面的超时时间增加倍数,可以学以致用到我们的业务开发中去。


→相关文章


【这次事故不仅仅是RestTemplate的锅还有@Async】


【上次被拉出去祭天,就是因为RestTemplate超时引发了血案】



参考资料


1、理解net.ipv4.tcp_syn_retries设置


https://www.dazhuanlan.com/2019/10/20/5dab43fbaadb1/


2、Linux 建立 TCP 连接的超时时间分析


http://www.chengweiyang.cn/2017/02/18/linux-connect-timeout/


3、Windows and Linux socket connection timeouts diffe


https://github.com/nodejs/node-v0.x-archive/issues/2324


4、Which is the default TCP connect timeout in Windows?


https://serverfault.com/questions/193160/which-is-the-default-tcp-connect-timeout-in-windows


5、TcpMaxConnectRetransmissions


http://systemmanager.ru/win2k_regestry.en/58804.htm


6、TcpInitialRTT


http://systemmanager.ru/win2k_regestry.en/58802.htm




回复公众号【资料】获得干货资料集锦:技术ppt、IT大会资料、架构、分布式资料等。
推荐好文
1、
互联网Code Review最佳实践分享
2、
dubbo面试题!会这些,说明你看懂了dubbo源码
3、
Kafka面试题!掌握它才说明你真正懂Kafka

4、

Netty 5.0为啥被舍弃?原因竟然是...

5、

中台之上——业务架构系列【汇总】
6、
必备瑞士军刀IDEA插件,你使用了哪些
7、
线上热更新代码只需3步 Arthas实战
8、
Eureka源码剖析之七:架构&面试题【总结】
9、
互联网工程师应该用这种姿势打印日志
10、
加入:互联网基础/架构交流群

998e0cbf1bc2ed98e4972fa18c3bc7df.png


-关注搬运工来架构,与优秀的你一同进步-


如果喜欢这篇文章可以点在看哦↘






推荐阅读
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
author-avatar
四季汤料_788
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有