问题描述
程序的架构是一个HTTP客户端的上报服务,一个HTTP服务端的接收服务,今天测试人员反映数据上报不上去了,请求全部失败
排查思路
1.查看客户端日志
发现所有的请求都是报错503,初步确定是服务端的问题,因为服务端使用了Zuul作为网关,503是我们自定义的超时状态码
2.查看网关Zuul的日志
发现在网关的断路器回调接口里面,报了大量的后端服务超时日志,确定是其中的一个物联平台服务,基本上hystrix的状态一直都是断开,直接返回的服务不可用。
3.登录eureka确定物联平台服务在哪台机器上
登录euraka,发现服务还在,确定服务的euraka心跳线程还活着,在euraka控制台找到出问题的服务的机器,登录这个机器排查原因
4.查看CPU和内存有没有大的波动
使用top
命令和free
命令,发现CPU和内存都挺稳定,排除了死循环
5.jps 查看java进程
jps的结果显示服务还在,并没有挂掉
6.查看出问题的服务的日志
发现只有eureka心跳打印的日志,猜测是其他线程被阻塞在某个资源上面了
7.jstack pid 查看线程快照
使用jstack pid > jstack.log
把线程快照保存到文件,便于拿到本地分析,但是这个东西第一次接触,是在是看不出有什么问题,只是看到了存在大量的WAITING的线程,
8.jmap查看堆内存情况
使用jmap -heap pid
命令查看堆内存,发现新生代Eden区占用90%,但是S0和S1基本上占用比较小,老年代占用40%多,确定不是内存溢出导致的
使用jstat -gc pid 2000 20
每个两秒打印GC情况,一共打印20次,发现堆内各个区域都不满,但是也没有回收,(因为线程都阻塞了嘛,所有请求都进不来)
9.回光返照
其实到这里我已经没有思路了,感觉像是玄学一样,确定是线程被阻塞,我只能在回去看jstack.log
文件,同时在网上查找这个文件能怎么用。忽然之间,我好像发现了一丝丝地异常
这个应该就是表示在8004这个端口上有很多的http线程处于WAITING状态,而8004端口上运行的恰好就是出问题的物联平台服务,到这里我忽然觉得问题明朗起来了,我想起来Tamcat有最大连接数这么一回事,在Spring boot里面貌似默认是200,那么问题是他为什么会被阻塞呢,是不是有的接口很慢???
再后来在网上看到可以使用netstat
查看网络状态,于是使用netstat | grep pid
,查看到在这个进程上确实有大量的连接,并且全部处于CLOSE_WAIT状态,细心的我会议起了TCP的三次握手和四次挥手
由上图可以看到,CLOSE_WAIT状态是客户端发起第一次挥手,向服务端发送FIN包,然后服务端向客户端发送ACK包,表示自己收到了断开连接的请求,但是自己还有数据没发送完(比如我服务里面哪个超时的接口),然后服务端就进入了CLOSE_WAIT状态,结果这个接口的响应时间实在是太长了,然后客户端等不及了,主动断开了连接,等到服务端终于执行完接口发送FIN包时,客户端早就把连接断开了,于是客户端回复的是一个RST包,表示我不认识你。
总结下来就是
在服务器与客户端通信过程中,因服务器发生了socket未关导致的CLOSE_WAIT发生,致使监听port打开的句柄数到了1024个,且均处于close_wait的状态,最终造成配置的port被占满出现“Too many open files”,无法再进行通信。
10.解决方案
出现这种问题基本上都是我们的程序的原因,解决方案有三步
- 修改超时的接口,优化代码逻辑,降低响应时间
- 设置服务端CLOSE_WAIT的超时时间
- 增大tamcat最大连接数