【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满

记一次 Java Web 服务请求僵死的情况,原因是因为请求线程池死锁占满,无法接收新的请求。以下为这次问题的排查过程:

1、网站告警某个实例无法响应,找到对应的实例,手动 curl 请求接口,无法响应。

2、使用 jps 命令找到对应进程的进程号。

3、使用 jstat 查看GC是否还正常执行,完整命令:jstat -gcutil 1624 1s,每隔 1s 打印一次内存及 gc 情况,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
从上图看出基本上内存没有啥变化,也不现执行 GC,一方面说明进程基本上没有执行啥任务;另一方面也可以反映 JVM 并没有完全僵死,还在存活(内存有少量的变化)。再看一张后面执行的 gc 情况,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
基本上也能看出进行了一次 GC,列 YGC 次数比上一张图片增加了一次。

4、再使用 jmap -heap 1647 确认 JVM 内存并没有压力,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满

5、查看一下线程的情况 jstack -l 1647 > jstack-20201224.log ,并将结果输出到文件中,方便排查。打开文件发现有大量以 qtp 为开头命名的线程,统计一下,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
经查,由于项目使用的是 jetty 容器,请求线程池中的线程命名就是这样的命名,很明显,线程被占满了,并且不释放。再看一下正常的 JVM 实例是什么样的?
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
线程数才 28 个。再确认下异常线程的状态:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满

6、打开刚才导出的查看线程的文件,找下具体什么原因导致线程无法释放,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
可以看到是因为线程阻塞在 BasicFuture.get() 方法处,进而看到自己的代码调用位置位于 ProcessTaskQueryResultService.java 的 198 行,如下图:
【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满
可以看到代码使用了 Apache 的 httpasyncclient 库的异步调用,返回 Future,如果请求异常,在此处就是请求了一个比较大的响应结果,接口比较耗费资源,导致无法响应,将一直阻塞在 Future.get() 处,最好的办法就是设置获取结果的超时时间,如上图改后的示例。

以上是 【Java】JAVA问题排查系列:Java Web 请求僵死,无法响应,线程被占满 的全部内容, 来源链接: utcz.com/a/91858.html

回到顶部