• 彻底理解并解决服务器出现大量TIME_WAIT


    为了能彻底讲清楚TIME_WAIT的原理及解决办法,本系列一共有4篇

    彻底理解并解决服务器出现大量TIME_WAIT - 第一篇_YZF_Kevin的博客-CSDN博客

    彻底理解并解决服务器出现大量TIME_WAIT - 第二篇_YZF_Kevin的博客-CSDN博客

    彻底理解并解决服务器出现大量TIME_WAIT - 第三篇_YZF_Kevin的博客-CSDN博客

    彻底理解并解决服务器出现大量TIME_WAIT - 第四篇_YZF_Kevin的博客-CSDN博客

    首先我们说下状态 TIME_WAIT 出现的原因

    TCP的新建连接,断开连接的流程和各个状态,如下图所示

    由上图可知:TIME_WAIT 是主动断开连接的一方会出现的,客户端,服务器都有可能出现

    当客户端主动断开连接时,发出最后一个ACK后就会处于 TIME_WAIT状态

    当服务器主动断开连接时,发出最后一个ACK后就会处于 TIME_WAIT状态

    结论:TIME_WAIT 是必然会出现的状态,是正常现象,且会定时回收

    TIME_WAIT 状态持续2MSL时间,MSL就是maximum segment lifetime(最大报文段的生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失(被丢弃)。RFC 793中规定MSL为2分钟,实际应用中,可能为30S,1分钟,2分钟。

    请注意两个状态,一个是TIME_WAIT,一个是CLOSE_WAIT,完全不同的两个状态

    TIME_WAIT 出现在主动断开方,发出最后一个ACK后

    CLOSE_WAIT 出现在被动断开方,收到主动断开方的FIN,发出自己的ACK后

    问题:为什么要有TIME_WAIT状态?

    1. 为了可靠地关闭TCP连接    

    举例:我们把主动断开连接的一方称为C端,被动断开连接的一方称为S端,由于网络不可靠,C端发送的最后一个ACK报文可能没成功发送到S端,那么S端就会重新发上一个报文即FIN,如果C端处于TIME_WAIT状态下,就可以重新发送报文ACK,然后重新计时2MSL时间才会进入CLOSED状态,S端收到ACK后就可以正常关闭TCP连接了。反之,如果这时C端处于 CLOSED 状态 ,就会响应 RST报文而不是ACK报文,那S端会认为这是一个错误,只能异常关闭TCP连接

    2. 防止上一次连接中的包,迷路后重新出现,影响新连接 

    由于网络的不可靠,TCP分节可能因为路由器异常而“迷途”,在迷途期间,TCP发送端会因确认超时而重发这个分节,这个分节最终被发送到对方时,对方可能已经是一个新的连接了,由此造成混乱。举例:关闭一个TCP链接后,马上又创建了一个相同的IP地址和端口之间的TCP链接,后一个链接被称为前一个链接的化身(incarnation),那么此时有可能出现这种状况,前一个链接的迷途重复分节在前一个链接终止后出现了,从而被误解成从属于新的连接的数据。为了不出现这种混乱,TCP不容许处于TIME_WAIT状态的连接立即启动一个新连接,由于TIME_WAIT状态持续2MSL,就能够保证当成功创建一个TCP链接的时候,来自前一个连接的迷途重复分节已经在网络中消逝

    注意close()shutdown()的区别

    close()其实只是将socket fd的引用计数减1,只有当该socket fd的引用计数减至0时,TCP传输层才会发起4次握手从而真正关闭连接。而shutdown则可以直接发起关闭连接所需的4次握手,而不用受到引用计数的限制

    close()会终止TCP的双工链路。由于TCP连接的全双工特性,可能会存在这样的应用场景:local peer不会再向remote peer发送数据,而remote peer可能还有数据需要发送过来,在这种情况下,如果local peer想要通知remote peer自己不会再发送数据但还会继续收数据这个事实,用close()是不行的,而shutdown()可以完成这个任务

    服务器短时间内大量的TIME_WAIT出现,才是问题

    会引发以下问题

    1. 由于处于TIME_WAIT状态,连接并未关闭,占据了大量的CPU,内存,文件描述符等,造成新的连接无法建立,客户端表现就是连接失败

    2. 如果服务器上同时有nginx,且nginx由于反向代理,那么还会占用很多端口(S端处于TIME_WAIT,该连接的另一方即C端需独占一个端口,C端是由nginx代理建立的),要知道端口是有限的,最多65535,一旦端口占用完,无论服务器配置如何高,新连接都无法建立了,客户端表现仍然是连接失败。而且即使端口没用完,因为建立连接时C端是随机一个位置,向后遍历查询可用端口的,此时就需要大量的遍历才能找到,效率上也很差

    短时间内大量TIME_WAIT出现的根本原因:高并发且持续的短连接

    1. 业务上使用了持续且大量的短连接,纯属设计缺陷,例如爬虫服务器就有可能出现这样的问题

    2. http请求中connection的值被设置成close,因为服务器处理完http请求后会主动断开连接,然后这个连接就处于TIME_WAIT状态了。持续时间长且量级较大的话,问题就显现出来了。http协议1.0中,connection默认为close,但在http1.1中connection默认行为是keep-alive,就是因为这个原因

    3. 服务器被攻击了,攻击方采用了大量的短连接

    重点:解决办法

    1. 代码层修改,把短连接改为长连接,但代价较大
    2. 修改 ip_local_port_range,增大可用端口范围,比如1024 ~ 65535
    3. 客户端程序中设置socket的 SO_LINGER 选项
    4. 打开 tcp_tw_recycle 和tcp_timestamps 选项,有一定风险,且linux4.12之后被废弃
    5. 打开 tcp_tw_reuse 和 tcp_timestamps 选项
    6. 设置 tcp_max_tw_buckets 为一个较小的值

    下一篇博客,我们将会一一讲解上面各个办法的原理

  • 相关阅读:
    java基于SpringBoot+vue的宠物销售商城网站 elementui
    C/C++ 单元自动化测试解决方案实践
    java——mybatis——Mybatis的缓存——Mybatis中的一级缓存——默认情况下,MyBatis 只开启一级缓存...
    【Java中23种面试常考的设计模式之适配器模式(Adapter)---结构型模式】
    synchronized原理-字节码分析、对象内存结构、锁升级过程、Monitor
    如何快速迁移服务器ubuntu的国内源
    【uniapp】开发app运行到手机预览(运行到安卓app基座)
    JUC第十二讲:JUC锁: 锁核心类AQS详解
    洛谷 P1967 [NOIP2013 提高组] 货车运输(最大生成树,最近公共祖先)
    QML之定位器(Column,Row,Flow,Grid)
  • 原文地址:https://blog.csdn.net/yzf279533105/article/details/126898099