最近在公司遇到CLOSE_WAIT过多的问题,现在解决后总结下,先说下CLOSE_WAIT产生的原因:
首先要知道客户端和服务端的连接是使用套接字通信的,TCP/IP协议建立连接需要三次握手,而关闭client与server的连接需要进行四步,如图:
建立连接后常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。
通过上图,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢?
就是服务端在被动关闭收到FIN,未发出自己FIN的情况下就处于CLOSE_WAIT状态了,通常CLOSE_WAIT的持续时间很
短,但是在某些特殊状态下就可能长时间存在,如果出现大量close_wait的现象,主要原因可能是一方关闭了socket链接,但是另一方
忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno( errno 是记录系统的最后
一次错误代码。),如果不是AGAIN,就断开连接。
在linux中查看socket状态的命令:
netstat -nt | awk '{wait[$NF]++}END{for(i in wait) print i,wait[i]}'
得到如下结果:
然后查看处于CLOSE_WAIT的都是哪些端口:
netstat -nt | awk '{if($NF=="CLOSE_WAIT"){wait[$5]++}}END{for(i in wait) print i,wait[i]}'
得到如下结果:
第一条命令会把当前所有的状态进行分类,第二个命令,他会定位到具体出问题的端口,再根据端口找到相应的程序,修改相应的程序即可。
上面还提到一种状态就是TIME_WAIT ,如果
TIME_WAIT
过多,也会对系统造成影响。TIME_WAIT 和CLOSE_WAIT过多
都会影响系统的扩展性,影响用户请求、其他系统对本系统的访问,这两种状态过多产生的影响一致,但不可用同样的方法去
解决,CLOSE_WAIT的情况需要从程序本身出发,而TIME_WAIT
更倾向于修改系统参数。
TIME_WAIT
产生的原因第一张图中已经说明了,是主动关闭的一方所处的状态,然后在保持这个状态2MSL(max segment lifetime)时间之
后,彻底关闭回收资源。为什么要等2MSL的时间呢?这是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:
1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就
会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很
大资源的,除非短时间内接受大量请求或者受到攻击。
<pre name="code" class="plain">#表示开启重用。允许将TIME_WAIT sockets重新用于新的TCP连接,默认为0,表示关闭 1、net.ipv4.tcp_tw_reuse = 1 #表示开启TCP连接中TIME_WAIT sockets的快速回收,默认为0,表示关闭 2、net.ipv4.tcp_tw_recycle = 1 #<span style="font-family:FangSong_GB2312;">表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN_WAIT_2状态的时间(可改为30,一般来说FIN_WAIT_2的连接也极少)</span> 3、net.ipv4.tcp_fin_timeout = 30 #<span style="font-family:SimHei;color:#333333;LINE-HEIGHT: 22px;">控制 TCP/IP 尝试验证空闲连接是否完好的频率</span> 4、net.ipv4.tcp_keepalive_time = 600 #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。 5、net.ipv4.tcp_max_syn_backlog = 8192 #表示系统同时保持TIME_WAIT的最大数量,如果超过这个数字,TIME_WAIT将立刻被清除并打印警告信息。默认为180000,改为60000。 6、net.ipv4.tcp_max_tw_buckets = 60000 #记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。 7、net.ipv4.tcp_max_syn_backlog = 65536 #每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。 8、net.core.netdev_max_backlog = 32768 #web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。 9、net.core.somaxconn = 32768 #定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。 10、net.core.wmem_default = 8388608 #该文件指定了接收套接字缓冲区大小的缺省值(以字节为单位)。 11、net.core.rmem_default = 8388608 #最大socket读buffer。 12、net.core.rmem_max = 16777216 #最大socket写buffer。 13、net.core.wmem_max = 16777216 #为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。 14、net.ipv4.tcp_synack_retries = 2 #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。 15、net.ipv4.tcp_syn_retries = 2 #表示开启TCP连接中TIME-WAITsockets的快速回收,默认为0,表示关闭 16、net.ipv4.tcp_tw_recycle = 1 #开启重用。允许将TIME-WAITsockets重新用于新的TCP连接。 17、net.ipv4.tcp_tw_reuse = 1 #同样有3个值,意思是:低于第一个值,TCP没有内存压力;在第二个值下,进入内存压力阶段;高于第三个值,TCP拒绝分配socket(内存单位是页)。 18、net.ipv4.tcp_mem = 94500000 915000000 927000000 #系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。 19、net.ipv4.tcp_max_orphans = 3276800 #每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。 20、net.core.netdev_max_backlog = 8096 #表示开启SYNCookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭。 21、net.ipv4.tcp_syncookies = 1
上面这些参数,如1、2、3、6、14、15、16、17等和CLOSE_WAIT或TIME_WAIT有关,优化相关参数让服务跑的更好。
名词解释: