问题现象:
服务端端口开着,客户端可以telnet 服务器上的端口,但访问服务时没有任何返回。
服务器环境 :Java 8 + linux CentOS release 6.5。
用apache ab压力测试工具对基于java web(spring boot) 的访问进行压力测试时当并发量为2000时经常发现请求无法跑完。
分析发现服务器上有很多web端口(8071)的连接,状态为:CLOSE_WAIT,且过很长时间都不会释放(1个小时以上甚至更久):
但在ab测试工具所在的客户端机器上并没有web端口的链接:
在客户端访问服务时没有任何返回,但客户端可以 telnet 上服务器上的web端口(8071),可以认为此时服务器假死。
一开始认为服务器上的open files开的太小不能再接收新的请求的连接,但用ulimit -a查看
服务器上的Open files时,发现其个数为32768已经调的足够大了(如下图),应该不是这个原因。
继续排查java启动时的输出文件(nohup.out),发现下面这个OutOfMemoryError错误:
Exception in thread "http-nio-8071-ClientPoller-1" 2018-03-22 10:35:43.958 [http-nio-8071-exec-1262] INFO cn.xxx..web.RedirectController - {"hostName":"localhost.localdomain","msg":"redirectUrl() is running..., uri: /NRr2EbY","traceId":"auto_gen_trc_9aa51ca9-8faa-4475-95e0-50892f321a86"}
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1378)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:163)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:141)
at org.apache.tomcat.util.net.AbstractEndpoint.processSocket(AbstractEndpoint.java:828)
at org.apache.tomcat.util.net.NioEndpoint$Poller.processKey(NioEndpoint.java:849)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:825)
at java.lang.Thread.run(Thread.java:748)
经研究,出现该异常可能是jvm内存太小,或者linux用户可以开启的线程数设置太小造成jvm无法启动所需的大量线程。之前的jvm启动时没有加任何内存配置,linux max user processes值为默认值1024,重新调整jvm启动参数,增大堆内存,缩小线程栈内存,重新启动程序:
nohup java -Xmx3800m -Xms3800m -Xmn2g -Xss256k -jar war &
同时修改文件:/etc/security/limits.d/90-nproc.conf 中的nproc值为31413(默认最大值的一半):
退出后重新登录查看max user processes已改变:
再次启动服务并进行压力测试,压力测试可以跑完,也没有发现状态为CLOSE_WAIT的链接,服务器假死问题解决。
总结:java web出现大量状态为CLOSE_WAIT的连接的原因是:tomcat在接收到新请求后由于无法开启新线程进行处理,造成客户端在主动断开连接后,服务器端无法进行连接释放造成的。和服务端的open files配置无关,因为不管有多少个请求连接(connection)在服务端只有一个端口在运行。
备注:在客户端用apache ab工具进行并发请求测试时,经常会报:
socket: Too many open files (24)
原因:客户端需要开启多个端口模拟多个用户连接,所有需要增大客户端机器的open files配置。