TCP TIME_WAIT

状态解释


如下图,TCP和谐握手姿势(下图摘抄至网络)

在下图中,纠正一些错误问题,个人认为关闭连接发起端进入到TIME_WAIT的状态时,等待的超时是60s,Linux默认的(至少CentOS7是这样的,源码位于tcp.h文件中定义的)超时结束正式CLOSED

TIME_WAIT状态是主动发起关闭连接一方存在的状态(有可能是客户端,也有可能服务端主动关闭)至少服务端主动关闭见到,但是不理解为什么主动关闭

大致过程是

  1. 发起方主动关闭,发送fin包至对端,同时状态转为FIN_WAIT1
  2. 对端接收方收到发起方的终止连接fin包后,并回执给发起方ack包,俗称确认包,同时状态转为CLOSE_WAIT状态
  3. 发起方收到接收方的回执ack包后,同时状态转为FIN_WAIT2,此时发起方等待接收方的最后一个关闭连接的fin包
  4. 在这个过程中,接收方有可能没有将数据处理完,在这个状态下一半称之为TCP连接半关闭状态,等到接收方处理完相应的数据,会回执给发送方最后一fin包,同时进入LAST_ACK状态
  5. 发送方接收到接收方回执的最后一个fin包正式关闭连接,同时将确认关闭连接的ack包发送给接收方,并进入TIME_WAIT状态,进入到TCP_TW_TIMEWAIT_LEN定义的超时时间,默认60s
  6. 接收方收到最一个ack包后,正式关闭此方的连接CLOSE

 

 原因


 造成大量TIME_WAIT的原因是什么

  1. 在生产环境存在大量的短连接请求
  2. 特别是HTTP请求中,如果connection header设置为close,通常由服务端主动关闭(未验证)
  3. 由于TIME_WAIT有超时设置,所以在服务器不断累积,产生很多TIME_WAIT连接

影响


 对系统的资源开销的影响

  1. 消耗大量socket套接字
  2. 消耗系统资源上下文切换
  3. 消耗TCP网络资源的开销端口
  4. 消耗文件句柄

作用


 TIME_WAIT的作用是什么

  1. 众所周知,TCP是一种可靠的传输协议,必须确保二个连接端全双工关闭,如果发送方的最后一个ack包丢失,这时发起方直接关闭的话,那么接收方没有收到这个ack包,那就就回重新发送fin包终止连接,这是发起方已经关闭了连接,正常关闭连接报错
  2. 处理延迟因网络抖动的数据报文情况,必须维持一个TIME_WAIT的状态,以避免误认为是新建的TCP连接的数据,总之解决网络中延迟到达的报文而预留的缓存时间

 措施


 如何应对此种情况,总结如下

  • 调整内核参数(适用于客户端&服务端),适当调整tw_buckets参数,默认是18000,当系统中的TIME_WAIT的连接大于设定的值,系统会删除部分TIME_WAIT连接,具体删除原理未验证,同时系统则抛出 TCP: time wait bucket table overflow,但这样会失去TIME_WAIT本身存在的意义,鱼和熊掌不可兼得

net.ipv4.tcp_max_tw_buckets = 5000

  • 通过编译内核(适用于客户端&服务端) https://github.com/torvalds/linux/blob/v4.12/include/net/tcp.h
    #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
                      * state, about 60 seconds    */
    /* 根据实际情况调整,不能调整的太小,推荐20-30比较合理
  • 客户端(主动连接一方)
  1. 增加端口
    net.ipv4.ip_local_port_range = 32768    60999
    # 32768 减少至20000,增加主动连接的端口
  2. 调整内核参数 net.ipv4.tcp_tw_reuse,开启TIME_WAIT连接复用功能
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_timestamps = 1
    
    # tcp_tw_reuse依赖于tcp_timestamp参数
    # 此参数内核默认是开启的
  3. HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间,减小TCP建立连接的开销
  4. 实在不行增加节点缓解业务系统压力
  • 服务端

   正常情况下都是客户端发起的主动关闭连接,个人理解服务端一般很少出现,如果NG HAPROXY可以会出现,因为存在大量的短连接

   但如果客户端发起的http请求header的connection参数设置了close,则,服务器处理完请求会自动断开连接,这种情况TIME_WAIT就留在了服务器

解决方法

  1. 原因在HTTP1.1协议中,http连接的header中Connection有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接。还有一个keep-alive的头,设置的值就代表了服务端保持连接保持多久,虽然HTTP默认Connection值为close,但是,现在的浏览器发送请求的时候一般都会设置Connection为keep-alive,服务器保留连接状态,等待让客户端关闭
  2. 实在不行就加机器,有钱啥都好办,横向扩展就是变向增加端口

参考文献


https://segmentfault.com/a/1190000022867113

https://blog.huoding.com/2013/12/31/316

原文地址:https://www.cnblogs.com/apink/p/15603710.html