• 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

    阅读原文

  • 相关阅读:
    FreeCAD二次开发-界面交互创建块工具
    vscode中使用git将自己的代码提交到码云
    js获取单页面参数(正则表达式)
    小程序的wx.request的封装
    前端实现截屏处理
    二维数组转化为json数组
    css更改滚动条样式
    盒子没有高度时填充背景图片
    页面初次渲染loading图
    layui分页的使用心得
  • 原文地址:https://www.cnblogs.com/276815076/p/16664341.html
Copyright © 2020-2023  润新知