TCP连接出现大量TimeWait状态的连接-原因解析

1,754 阅读3分钟

一. 问题背景

应用在高并发运行情况下出现大量TimeWait连接请求

# 统计命令:  
# 统计已连接上的,状态为“established  
netstat -na|grep ESTABLISHED|wc -l
# 查看80端口连接数
netstat -ant|grep -i "80"|wc -l
# 如果需要统计tcp连接每种状态的连接的数量
netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

短时间后,所有的TIME_WAIT全部消失,被回收,端口包括服务,均正常。 即,在高并发的场景下,TIME_WAIT连接存在,属于正常现象。 线上场景中,持续的高并发场景

  • 一些部分TIME_WAIT连接被回收,但新的TIME_WAIT连接产生;
  • 一些极端情况下,会出现大量的TIME_WAIT

在出现大量TIME_WAIT的情况下,因为linux是基于文件的操作系统,维护每一个socket都需要创建一个文件,而每个进程做大能够打开的文件数是65535个,如果大于这个数量,如果使用Nginx这样的web服务器作为反向代理就会发生诸如:addres already in use:connect 异常。

二. 问题分析

  • 大量的##短连接##存在
  • 特变是HTTP请求中,如果connection头部取值被设置为close时,基本都是【服务端】发起主动关闭连接
  • 而,TCP四次挥手关闭连接机制中,为了保证ACK重发丢弃延迟数据,设置time_wait为2倍的MSL(报文最大存活时间)
  • 保持2个MSL时间,即4分钟;

三. 解决办法

  • 客户端,HTTP请求的头部,connection设置为keep-alive,保持存活一段时间:现在的浏览器,一般都这么进行了
  • 服务器端,
    • 允许time_wait状态的socket被重用
    #让TIME_WAIT状态可以重用,这样即使TIME_WAIT占满了所有端口,也不会拒绝新的请求造成障
    echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
    #让TIME_WAIT尽快回收,我也不知是多久,观察大概是一秒钟
    echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle
    
    • 所见time_wait时间,设置为1MSL *time_wait状态,存在的必要性
  • 可靠的实现 TCP 全双工连接的终止:四次挥手关闭 TCP 连接过程中,最后的 ACK 是由「主动关闭连接」的一端发出的,如果这个 ACK 丢失,则,对方会重发 FIN 请求,因此,在「主动关闭连接」的一段,需要维护一个 time_wait 状态,处理对方重发的 FIN 请求;
  • 处理延迟到达的报文:由于路由器可能抖动,TCP 报文会延迟到达,为了避免「延迟到达的 TCP 报文」被误认为是「新 TCP 连接」的数据,则,需要在允许新创建 TCP 连接之前,保持一个不可用的状态,等待所有延迟报文的消失,一般设置为 2 倍的 MSL(报文的最大生存时间),解决「延迟达到的 TCP 报文」问题;

附录:TCP三次挥手和四次握手

详细细节,参考:

  • TCP的三次握手与四次挥手(详解+动图) 具体示意图:
  1. 三次握手,建立连接过程
  2. 四次挥手,释放连接过程 几个核心疑问:
  3. time_wait 是「服务器端」的状态?or 「客户端」的状态? RE:time_wait 是「主动关闭 TCP 连接」一方的状态,可能是「客服端」的,也可能是「服务器端」的
  4. 一般情况下,都是「客户端」所处的状态;「服务器端」一般设置「不主动关闭连接」