nginx:TIME_WAIT过多或者CLOSE_WAIT过多的状态

1 起因

线上服务器nginx日志运行一段时间后,会报如下错误:

1024 worker_connections are not enough

一般做法是修改worker_connections。
但实际上:该服务是用于时间比较短的连接里,并且一天最多才4000个请求。不可能会耗尽worker_connections。
除非每次连接都没有释放对应的连接。

shell>netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
CLOSE_WAIT 802
ESTABLISHED 106
shell>lsof -n | grep “nginx对应的一个进程id”
MvLogServ 31125 mv 111u IPv4 76653578 0t0 TCP 10.1.138.60:8996->10.1.138.60:51977 (CLOSE_WAIT)
MvLogServ 31125 mv 112u IPv4 76659698 0t0 TCP 10.1.138.60:8996->10.1.138.60:52015 (CLOSE_WAIT)
MvLogServ 31125 mv 113u IPv4 76662836 0t0 TCP 10.1.138.60:8996->10.1.138.60:52042 (CLOSE_WAIT)
MvLogServ 31125 mv 114u IPv4 76663435 0t0 TCP 10.1.138.60:8996->10.1.138.60:52051 (CLOSE_WAIT)
MvLogServ 31125 mv 115u IPv4 76682134 0t0 TCP 10.1.138.60:8996->10.1.138.60:52136 (CLOSE_WAIT)
MvLogServ 31125 mv 116u IPv4 76685095 0t0 TCP 10.1.138.60:8996->10.1.138.60:52159 (CLOSE_WAIT)
……………….

TIME_WAIT:表示主动关闭,通过优化系统内核参数可容易解决。
CLOSE_WAIT:表示被动关闭,需要从程序本身出发。
ESTABLISHED:表示正在通信
则可知:nginx:CLOSE_WAIT过多的状态

2 解决

2.1 TIME_WAIT 通过优化系统内核参数可容易解决

TIME_WAIT大量产生很多通常都发生在实际应用环境中。
TIME_WAIT产生的原因:在通讯过程中A主动关闭造成的,
在A发送了最后一个FIN包后,系统会等待 Double时间
的MSL(Max Segment Lifetime)【注:按不同的操作系统有不同时间】用于等待接受B发送过来的FIN_ACK和FIN,
这段时间A的对应的socket的fd是不能够重新利用的,
这样在大量的短连接服务中,会出现TIME_WAIT过多的现象。

解决方案:
调整TIME_WAIT超时时间
vi /etc/sysctl.conf
#表示开启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
#表示如果套接字由本端要求关闭。
#这个参数决定了它保持在FIN-WAIT-2状态的时间
#生效,如下命令
/sbin/sysctl -p


  • 注:
    已经主动关闭连接了为啥还要保持资源一段时间呢?
    这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

  1. 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    即:允许老的重复分节在网络中消逝。
  2. 可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。
    另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。
    即:可靠地实现TCP全双工连接的终止。(确保最后的ACK能让被关闭方接收)

2.2 CLOSE_WAIT 需要从程序本身出发

LOSE_WAIT产生的原因是客户端B主动关闭,
服务器A收到FIN包,应用层却没有做出关闭操作引起的。
CLOSE_WAIT在Nginx上面的产生原因还是因为Nagle’s算法加Nginx本身EPOLL的ET触发模式导致。

ET出发模式在数据就绪的时候会触发一次回调操作,Nagle’s算法会累积TCP包,如果最后的数据包和

FIN包被Nagle’s算法合并,会导致EPOLL的ET模式只触发一次。
然而在应用层的SOCKET是读取返回0才代表链接关闭,
而读取这次合并的数据包时是不返回0的,
然后SOCKET以后都不会触发事件,
所以导致应用层没有关闭SOCKET,
从而产生大量的CLOSE_WAIT状态链接。
关闭TCP_NODELAY,在Nginx配置中加上

tcp_nodelay on;

3 总结

  • TIME_WAIT状态可以通过优化服务器参数得到解决。
    因为发生TIME_WAIT的情况是服务器自身可控的,
    要么就是对方连接的异常,要么就是自己没有迅速回收资源,
    总之不是由于自己程序错误导致的。

  • CLOSE_WAIT需要通过程序本身。
    如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。
    即在对方连接关闭之后,程序里没有检测到,或者程序没有关闭连接,于是这个资源就一直被程序占着。
    服务器对于程序抢占的资源没有主动回收的功能。只能修改程序本身。
    代码需要判断socket,一旦读到0,断开连接,read返回负,
    检查一下errno,如果不是AGAIN,就断开连接。

参考来源:
【1】http://www.cnblogs.com/Bozh/p/3752476.html
作者联系方式:Email:zhangbolinux@sina.com QQ:513364476
【2】http://itindex.net/detail/50213-%E6%9C%8D%E5%8A%A1%E5%99%A8-time_wait-close_wait

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值