nginx到tomcat有大量CLOSE_WAIT状态连接

nginx到tomcat有大量CLOSE_WAIT状态连接

  1. 原因总结:

    1. 资源未及时释放

      1. 数据库连接未及时释放,数据库连接池满后新的请求阻塞:https://blog.csdn.net/yu616568/article/details/44677985

      2. httpclient创建的socket连接未及时释放,连接池占用满:https://blog.csdn.net/zhang4852898/article/details/91607636

        如:当Server的后端用client类别连到Remote端,连线逾时遭到Remote端断线,此时被动关闭连线时,后端若没处理好关闭,就会造成CLOSE_WAIT

      3. 代码中存在线程锁但执行玩后未及时释放,新的请求阻塞:https://blog.csdn.net/q512224549/article/details/89483432

    2. nginx配置引用lua脚本获取资源延迟,客户端主动断开了连接:https://www.cnblogs.com/husbandmen/p/10116285.html

    3. 服务端接口代码不规范,服务端接口处理耗时较长,客户端主动断开了连接:https://www.cnblogs.com/grey-wolf/p/10936657.html

      1. 如jvm GC 时间过长、服务器load 太高、tomcat jvm 内存溢出
    4. 某一版Tomcat或JVM的bug造成無法關閉socket:https://www.cnblogs.com/saaav/p/6258831.html

    5. 几乎不存在的情况:程序存在死循环

  2. 原因3问题复现:https://www.cnblogs.com/grey-wolf/p/10936657.html

    1. 通过Linux服务器部署一个post的demo,以博客源码建立一个http的post请求进行了一个复现。

      1. 服务器通过netstat -anop|grep 服务器ip:8280,建立连接后为ESTABLISHED,客户端10s后断开连接后状态是CLOSE_WAIT,服务器线程休眠结束回传数据后连接消失
      2. 客户端通过netstat -ano|findstr 服务器ip:8280,建立连接后为ESTABLISHED,客户端10s后断开连接后状态是FIN_WAIT_2,服务器线程休眠结束回传数据后连接消失
      3. 判断服务器线程休眠结束能正常回传数据是通过wireshark抓包知晓
      • tomcat常用的三个状态是:

        • ESTABLISHED 正在通信
        • TIME_WAIT 主动关闭
        • CLOSE_WAIT 表示被动关闭
  3. 问题排查:wireshark抓包,分析dump日志等

    • tcp状态变迁图

      nginx到tomcat有大量CLOSE_WAIT状态连接_第1张图片

      重点关注下面红色部分:

      nginx到tomcat有大量CLOSE_WAIT状态连接_第2张图片

      当一个tcp实体建立连接后,一直处于下面的established状态。

      上图红色框框起来的部分表示:

      收到客户端的FIN(上图红色的recv:FIN),服务端回应ACK。(上图红色的send:ACK)。然后服务端进入了CLOSE_WAIT状态。

      • 然后在这个阶段服务端因为上述各种原因阻塞,无法返回新的FIN,tcp连接阻塞,且没有及时断开清理这些没有响应的连接,请求不断增多,阻塞的连接数也越多,直到tomcat连接池用完使tomcat假死。
  4. 网上找到的解决方案:

    1. 修改程序代码,及时释放使用资源

    2. 修改Linux配置文件,使服务器及时回收超时连接。(无效)

      1. vim /etc/sysctl.conf在文件末端加上以下内容:

        net.ipv4.tcp_syncookies = 1	#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
        net.ipv4.tcp_tw_reuse = 1	#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
        net.ipv4.tcp_tw_recycle = 1	#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
        net.ipv4.tcp_fin_timeout = 30	#修改系統默认的 TIMEOUT 时间
        
        • 结果:无效,该参数是对于断开请求发起方的配置,在复现场景中服务器为断开请求的接收方

          在复现场景中,当30s后客户端发出FIN请求断开后,服务端进入CLOSE_WAIT状态,再过了30s后,如果tcp_fin_timeout参数生效,服务端应处于LAST_ACK状态或无tcp连接,但一直到120s后接口休眠结束处理完请求发出FIN,但断开发起方即客户端已断开,故服务器才处于LAST_ACK状态,一段时间后TCP连接被回收,在休眠期间服务器一直为CLOSE_WAIT状态

        net.ipv4.tcp_keepalive_time = 30	#启用 keepalive 时,TCP 30发送一次 keepalive 消息。
        #probe2次(每次2秒)不成功,内核才彻底放弃,认为该连接已失效
        net.ipv4.tcp_keepalive_probes = 2
        net.ipv4.tcp_keepalive_intvl = 2
        
        • 结果:无效,jmeter中无论是否勾选了Keepalive都不不生效

          在复现场景中,当30s后客户端发出FIN请求断开后,服务端进入CLOSE_WAIT状态,再大概过了34s后,如果tcp_keepalive_time参数生效,服务端应处于LAST_ACK状态或无tcp连接,但一直到120s后接口休眠结束处理完请求发出FIN,但断开发起方即客户端已断开,故服务器才处于LAST_ACK状态,一段时间后TCP连接被回收,在休眠期间服务器一直为CLOSE_WAIT状态

      2. 执行 /sbin/sysctl -p 让参数生效

      3. 最终结果:无效,只有当接口处理请求完成后,系统才会回收这些TCP连接,如果接口存在异常则连接一直存在

        在复现场景中,未配置参数的情况下,当10s后客户端发出FIN请求断开后,服务端进入CLOSE_WAIT状态,再过了50s后,接口休眠结束处理完请求发出FIN,但断开发起方即客户端已断开,故服务器处于LAST_ACK状态,一段时间后TCP连接被回收

    3. 由应用层如tomcat发出fin标志(无论是代码结束正常发送还是超时后发送),使传输层的tcp连接断开,接收关闭请求方(异常的服务端)结束CLOSE_WAIT状态

      1. 思路:设置tomcat配置文件中的connectionTimeout和keepAlivetimeout属性
        1. 结果:无论设置两个时间变长还是变短都无效
    4. 暴力杀死进程法:

      1. https://github.com/rghose/kill-close-wait-connections
      2. https://www.experts-exchange.com/questions/20568402/How-to-clear-CLOSE-WAIT-state-of-a-TCP-connection.html
      3. http://www.shellhacks.com/en/HowTo-Kill-TCP-Connections-in-CLOSEWAIT-State

你可能感兴趣的:(工作必修,nginx,tomcat,CLOASE_WAIT,TCP,Linux)