TCP TIME_WAIT
状态解释
如下图,TCP和谐握手姿势(下图摘抄至网络)
在下图中,纠正一些错误问题,个人认为关闭连接发起端进入到TIME_WAIT的状态时,等待的超时是60s,Linux默认的(至少CentOS7是这样的,源码位于tcp.h文件中定义的)超时结束正式CLOSED
TIME_WAIT状态是主动发起关闭连接一方存在的状态(有可能是客户端,也有可能服务端主动关闭)至少服务端主动关闭见到,但是不理解为什么主动关闭
大致过程是
- 发起方主动关闭,发送fin包至对端,同时状态转为FIN_WAIT1
- 对端接收方收到发起方的终止连接fin包后,并回执给发起方ack包,俗称确认包,同时状态转为CLOSE_WAIT状态
- 发起方收到接收方的回执ack包后,同时状态转为FIN_WAIT2,此时发起方等待接收方的最后一个关闭连接的fin包
- 在这个过程中,接收方有可能没有将数据处理完,在这个状态下一半称之为TCP连接半关闭状态,等到接收方处理完相应的数据,会回执给发送方最后一fin包,同时进入LAST_ACK状态
- 发送方接收到接收方回执的最后一个fin包正式关闭连接,同时将确认关闭连接的ack包发送给接收方,并进入TIME_WAIT状态,进入到TCP_TW_TIMEWAIT_LEN定义的超时时间,默认60s
- 接收方收到最一个ack包后,正式关闭此方的连接CLOSE
原因
造成大量TIME_WAIT的原因是什么
- 在生产环境存在大量的短连接请求
- 特别是HTTP请求中,如果connection header设置为close,通常由服务端主动关闭(未验证)
- 由于TIME_WAIT有超时设置,所以在服务器不断累积,产生很多TIME_WAIT连接
影响
对系统的资源开销的影响
- 消耗大量socket套接字
- 消耗系统资源上下文切换
- 消耗TCP网络资源的开销端口
- 消耗文件句柄
作用
TIME_WAIT的作用是什么
- 众所周知,TCP是一种可靠的传输协议,必须确保二个连接端全双工关闭,如果发送方的最后一个ack包丢失,这时发起方直接关闭的话,那么接收方没有收到这个ack包,那就就回重新发送fin包终止连接,这是发起方已经关闭了连接,正常关闭连接报错
- 处理延迟因网络抖动的数据报文情况,必须维持一个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比较合理 - 客户端(主动连接一方)
- 增加端口
net.ipv4.ip_local_port_range = 32768 60999 # 32768 减少至20000,增加主动连接的端口
- 调整内核参数 net.ipv4.tcp_tw_reuse,开启TIME_WAIT连接复用功能
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_timestamps = 1 # tcp_tw_reuse依赖于tcp_timestamp参数 # 此参数内核默认是开启的
- HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间,减小TCP建立连接的开销
- 实在不行增加节点缓解业务系统压力
- 服务端
正常情况下都是客户端发起的主动关闭连接,个人理解服务端一般很少出现,如果NG HAPROXY可以会出现,因为存在大量的短连接
但如果客户端发起的http请求header的connection参数设置了close,则,服务器处理完请求会自动断开连接,这种情况TIME_WAIT就留在了服务器
解决方法
- 原因在HTTP1.1协议中,http连接的header中Connection有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接。还有一个keep-alive的头,设置的值就代表了服务端保持连接保持多久,虽然HTTP默认Connection值为close,但是,现在的浏览器发送请求的时候一般都会设置Connection为keep-alive,服务器保留连接状态,等待让客户端关闭
- 实在不行就加机器,有钱啥都好办,横向扩展就是变向增加端口
参考文献
https://segmentfault.com/a/1190000022867113
https://blog.huoding.com/2013/12/31/316