nginx到tomcat有大量CLOSE_WAIT状态连接
-
原因总结:
-
资源未及时释放
-
数据库连接未及时释放,数据库连接池满后新的请求阻塞:https://blog.csdn.net/yu616568/article/details/44677985
-
httpclient创建的socket连接未及时释放,连接池占用满:https://blog.csdn.net/zhang4852898/article/details/91607636
如:当Server的后端用client类别连到Remote端,连线逾时遭到Remote端断线,此时被动关闭连线时,后端若没处理好关闭,就会造成CLOSE_WAIT
-
代码中存在线程锁但执行玩后未及时释放,新的请求阻塞:https://blog.csdn.net/q512224549/article/details/89483432
-
-
nginx配置引用lua脚本获取资源延迟,客户端主动断开了连接:https://www.cnblogs.com/husbandmen/p/10116285.html
-
服务端接口代码不规范,服务端接口处理耗时较长,客户端主动断开了连接:https://www.cnblogs.com/grey-wolf/p/10936657.html
- 如jvm GC 时间过长、服务器load 太高、tomcat jvm 内存溢出
-
某一版Tomcat或JVM的bug造成無法關閉socket:https://www.cnblogs.com/saaav/p/6258831.html
-
几乎不存在的情况:程序存在死循环
-
-
原因3问题复现:https://www.cnblogs.com/grey-wolf/p/10936657.html
-
通过Linux服务器部署一个post的demo,以博客源码建立一个http的post请求进行了一个复现。
- 服务器通过
netstat -anop|grep 服务器ip:8280
,建立连接后为ESTABLISHED,客户端10s后断开连接后状态是CLOSE_WAIT,服务器线程休眠结束回传数据后连接消失 - 客户端通过
netstat -ano|findstr 服务器ip:8280
,建立连接后为ESTABLISHED,客户端10s后断开连接后状态是FIN_WAIT_2,服务器线程休眠结束回传数据后连接消失 - 判断服务器线程休眠结束能正常回传数据是通过wireshark抓包知晓
-
tomcat常用的三个状态是:
- ESTABLISHED 正在通信
- TIME_WAIT 主动关闭
- CLOSE_WAIT 表示被动关闭
- 服务器通过
-
-
问题排查:wireshark抓包,分析dump日志等
-
tcp状态变迁图
重点关注下面红色部分:
当一个tcp实体建立连接后,一直处于下面的established状态。
上图红色框框起来的部分表示:
收到客户端的FIN(上图红色的recv:FIN),服务端回应ACK。(上图红色的send:ACK)。然后服务端进入了CLOSE_WAIT状态。
- 然后在这个阶段服务端因为上述各种原因阻塞,无法返回新的FIN,tcp连接阻塞,且没有及时断开清理这些没有响应的连接,请求不断增多,阻塞的连接数也越多,直到tomcat连接池用完使tomcat假死。
-
-
网上找到的解决方案:
-
修改程序代码,及时释放使用资源
-
修改Linux配置文件,使服务器及时回收超时连接。(无效)
-
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状态
-
-
执行
/sbin/sysctl -p
让参数生效 -
最终结果:无效,只有当接口处理请求完成后,系统才会回收这些TCP连接,如果接口存在异常则连接一直存在
在复现场景中,未配置参数的情况下,当10s后客户端发出FIN请求断开后,服务端进入CLOSE_WAIT状态,再过了50s后,接口休眠结束处理完请求发出FIN,但断开发起方即客户端已断开,故服务器处于LAST_ACK状态,一段时间后TCP连接被回收
-
-
由应用层如tomcat发出fin标志(无论是代码结束正常发送还是超时后发送),使传输层的tcp连接断开,接收关闭请求方(异常的服务端)结束CLOSE_WAIT状态
- 思路:设置tomcat配置文件中的connectionTimeout和keepAlivetimeout属性
- 结果:无论设置两个时间变长还是变短都无效
- 思路:设置tomcat配置文件中的connectionTimeout和keepAlivetimeout属性
-
暴力杀死进程法:
-