TIME WAIT 和CLOSE WAIT的区别
昨天京东一面,问了一个很简单的问题。结果自己只是简单回答了一下并没有深入分析出现问题的原因以及解决的办法。感觉这个问题被问的频率还是挺高的,所以下来后自己看了博客,稍微整理了一下。
现象:tomcat崩溃,抛出大量Too Many Open Files异常。
这个时候使用netstat命令去查看服务器的网络连接状态会发现服务器可能维持了大量的TIME WAIT 或者CLOSE WAIT状态。
这两种状态产生的原因是不一样的。先来谈谈TIME WAIT状态
首先,TCP连接在断开的过程中是四次挥手,主动断开连接和被动断开连接所要维持的状态是不一样。
先来说说TIME WAIT。如果是主动断开连接的一方,在最后一次发送完ACK之后需要维持一个2MSL时间的TIME WAIT状态,为什么?原因是最后一次发送完ACK之后,由于网络的不安全性,并不能保证ACK可以准确无误的顺利到达接收端,假如发送完ACK之后,直接进入CLOSED状态,刚好此时网络出现故障,ACK丢失了,则接收端会超时重传FIN给发送端,由于发送端已经关闭了,就会向接收端回复rst。导致接收端不能正常关闭连接,造成资源浪费。这里还有一个问题,为什么是2MSL呢?目的是为了防止上一次连接中因为网络故障延迟到达的数据重新出现,影响新连接,经过2MSL后,上一次连接的所有数据包会全部消失。基于TCP的Htpp协议主动关闭连接的是Server端,因此Server端会维持大量的TIME WAIT。因此Http1.1使用长连接,复用TCP的reques/response,也正是发现了这个问题所在。
解决方法:让服务器快速的回收和重用TIME WAIT资源。
下面来看一下我们网管对/etc/sysctl.conf文件的修改:
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
##减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
##优化网络设备接收队列
net.core.netdev_max_backlog=3000
修改完之后执行/sbin/sysctl -p让参数生效。
第二个状态就是CLOSE WAIT状态
在断开连接的时候,被动关闭连接的一方可能会出现的一种状态。如果服务器出现了一直保持CLOSE WAIT状态,则说明服务器在对方关闭连接之后,自己程序里没有检测到或者忘记关了。也就是说这是程序代码的问题。因此不能再像TIME WAIT调整参数来解决了。只能通过终止程序。所以,如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。