tomcat请求无响应假死状态
简单说下程序部署情况:tomcat + oracle
排查过程:
排查时,可以使用命令进行排查,也可以使用可视化监控工具;例如使用使用 JDK 自带的 jvisualvm.exe 监控工具。
命令排查过程:
1、请求服务无响应,首先看看 tomcat 是否是真的挂掉了:命令:ps -ef | grep tomcat
通过上面的命令查看 tomcat 运行着;执行结果如下:
通过命令查看发现,tomcat 正常运行着,那么这就是处于假死状态,下面接着排查。
2、查看 http 请求是否到达了 tomcat:通过查看 tomcat 的 logs 目录下的 localhost_access_log 日志文件中 请求记录;
命令:tail -100f localhost_access_log
通过上面的命令查看实时的日志,执行完上面的查看日志的命令后,然后再次请求下程序,在日志中并没有发现请求记录,说明 tomcat 处于一种假死状态,下面接着排查。
3、查看 tomcat 的 JVM 的 GC 情况:查看 GC 情况,是否由于频繁的 GC,长时间的 GC,导致程序出现长时间的卡顿,最终导致请求来不及处理,进入队列中进行等待,调用方长时间得不到响应,造成 tomcat 假死状态;
命令:jstat -gc pid time count
例如:_ jstat -gc 71129 1000 5 _ 监控 71129 这个进程 JVM 的 GC 情况,每隔 1000ms 输出一次,共输出 5 次;
命令执行结果参数解析:
通过上面命令查看 GC 情况,发现垃圾回收也不频繁,并且进行 GC 的时间也不长,应该不是 GC 的原因。
4、查看 tomcat 的 JVM 的堆情况:查看堆内存的情况,是否存在堆内存溢出 导致 tomcat 假死,无法为新请求分配堆内存资源;
命令 :jmap -heap pid
例子:jmap -heap 71129 71129 是正在运行 tomcat 的进程号 ;
通过命令执行结果得知,堆内存中可使用内存还很大,不会出现内存溢出的问题,所以也不是堆内存过小导致的 tomcat 假死。
5、查看 tomcat 的 JVM 线程情况:①、使用 jstack 命令导出当前 JVM 的线程 dump 快照,然后看看 dump 中线程都在干什么?
命令:jstack pid >> jvmThreadDump.log
例子:jstack 71129 >> jvmThreadDump.log
生成 71129 进程的 JVM 的线程快照,并将快照内容重定向到 jvmThreadDump.log 文件中;
注意:生成的 jvmThreadDump.log 在你当前执行命令的目录下。
②、接着使用命令 more 查看 jvmThreadDump.log 内容;
命令 :more jvmThreadDump.log
如果的 dump 文件太大的话,需要使用 more 命令一点点看;执行完 more 命令的话,再按 enter 回车键 一点点展示文件内容;
③、通过查看线程快照文件,发现很多线程的状态是 WAITING 等待状态;
并且使用命令查看线程状态为 WAITING 的线程占总线程的比例:
注意:tomcatDump.log 为生成的线程快照文件名称,记得改为自己设置的名称 ;count=cat tomcatDump.log | grep java.lang.Thread.State | wc -l
; wait=cat tomcatDump.log | grep WAITING | wc -l
; a=echo | awk "{print $wait/$count*100}"
; echo "$a%" 执行命令,得到结果 :_ 91.9786% _ ,发现九成多的线程处于等待状态;
至此,找到了 tomcat 假死的原因,但是还需进一步确定 什么原因导致的大量线程一直等待?
通过查看调用的服务接口代码得知,此接口业务逻辑中自己没设置任何的锁,所以应该不是自己写的代码的问题,但是此接口中涉及到了很多 JDBC 数据库操作,那是不是数据库连接池中的连接不够用了呢?因为数据库连接属于竞争资源,如果连接池中的连接已经耗尽了,那么接下来的进行 JDBC 的线程就需要进行 wait 等待连接。
6、查看与数据库建立的 TCP 连接情况:在上面发现,大量线程处于等待状态,而通过分析得知,可能是由于数据库连接池中的连接耗尽导致的,所以可以通过命令查看下,部署服务代码的服务器与数据库所在服务器建立的 TCP 连接数是否已经达到了配置的数据库连接池的最大连接数;
命令:netstat -pan | grep 1521 | wc -l
因为本文中使用的数据库是 Oracle,所以 grep 搜索匹配的端口号是 1521;
如果是 mysql 数据库则将端口号改为 3306 即可, netstat -pan | grep 3306 | wc -l ;
如果设置了自定义的数据库端口号,则改为自定义的端口号即可;
通过命令查询到 已经使用的数据库的连接数为 6 个,那接着看下设置的数据库连接池最大连接数;
数据源配置如下:
至此,tomcat 假死的排查过程就结束了,并且原因也找到了,就是数据库连接池中的连接耗尽了;所以,在后面测试中,需要在数据源中将最大连接数设置的大一些,并且也再进一步查看下代码,看看是否存在数据库连接使用完后没有进行关闭的问题。
除了数据库连接池连接耗尽会导致 tomcat 假死外,还有一些其它的情况也会导致发生,例如:redis 连接池连接耗尽,或者是 redis 连接使用完不释放,最终导致 redis 连接耗尽。
除了使用上面的命令进行问题排查外,也可以直接使用可视化监控工具进行排查,更加简便、直观。
可视化监控工具排查 使用 JDK 自带的 jvisualvm.exe 工具进行 JMX 远程 可视化监控 tomcat;
jvisualvm.exe 位于 $JAVA_HOME/bin 目录下;
1、使用 JMX 实现远程监控步骤:下面使用 JMX 实现远程监控的内容参考自:jvisualvm 远程监控 tomcat
①、在 Tomcat 的 bin 目录下的 startup.sh 文件中的倒数第二行(也就是 exec “[Math Processing Error]EXECUTABLE” start “$@” 一行上边)加上如下内容:
export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.1.130 -Dcom.sun.management.jmxremote.port=7003 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" 上面内容参数解析:
②、将 jvisualvm.exe 打开,界面如下:
③、在远程上右击,添加主机,输入服务器的 ip:就是在 startup.sh 文件中添加内容中的 hostname
⑤、通过上面的步骤,就已经完成了远程监控连接了,然后自己双击就能进行监控界面了:
2、查看监视内容:
通过查看监视画面得知,CPU、GC、堆 Heap 的情况都没有问题,那接着查看下线程的情况:
点击上面图片中的 线程 Dump 按钮,生成线程的快照,快照文件内容部分如下:
通过查看快照文件内容发现,很多线程的状态的都是 WAITING 等待状态;
接下来的分析排查过程就如上面的 命令排查过程 一样了。
总结
上面的两种排查方式,本人比较推荐还是使用第一种 命令排查 ,因为很多的情况是不会让你修改配置文件进行远程监控的,即便使用监控工具看起来更加直观、简便;所以,平时需要记一些常用的排查命令,以备不时之需。