【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
记一次 Java Web 服务请求僵死的情况,原因是因为请求线程池死锁占满,无法接收新的请求。以下为这次问题的排查过程:
1、网站告警某个实例无法响应,找到对应的实例,手动 curl
请求接口,无法响应。
2、使用 jps
命令找到对应进程的进程号。
3、使用 jstat
查看GC是否还正常执行,完整命令:jstat -gcutil 1624 1s
,每隔 1s 打印一次内存及 gc 情况,如下图:
从上图看出基本上内存没有啥变化,也不现执行 GC,一方面说明进程基本上没有执行啥任务;另一方面也可以反映 JVM 并没有完全僵死,还在存活(内存有少量的变化)。再看一张后面执行的 gc 情况,如下图:
基本上也能看出进行了一次 GC,列 YGC 次数比上一张图片增加了一次。
4、再使用 jmap -heap 1647
确认 JVM 内存并没有压力,如下图:
5、查看一下线程的情况 jstack -l 1647 > jstack-20201224.log
,并将结果输出到文件中,方便排查。打开文件发现有大量以 qtp 为开头命名的线程,统计一下,如下图:
经查,由于项目使用的是 jetty 容器,请求线程池中的线程命名就是这样的命名,很明显,线程被占满了,并且不释放。再看一下正常的 JVM 实例是什么样的?
线程数才 28 个。再确认下异常线程的状态:
6、打开刚才导出的查看线程的文件,找下具体什么原因导致线程无法释放,如下图:
可以看到是因为线程阻塞在 BasicFuture.get()
方法处,进而看到自己的代码调用位置位于 ProcessTaskQueryResultService.java
的 198 行,如下图:
可以看到代码使用了 Apache 的 httpasyncclient 库的异步调用,返回 Future,如果请求异常,在此处就是请求了一个比较大的响应结果,接口比较耗费资源,导致无法响应,将一直阻塞在 Future.get()
处,最好的办法就是设置获取结果的超时时间,如上图改后的示例。
以上是 【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满 的全部内容, 来源链接: utcz.com/a/91858.html