Tomcat实例中特定webapp异常故障排查方案咨询
哥们,这种情况我遇到过好几次——单个Tomcat应用挂掉但其他正常,重启应用没用必须重启容器,十有八九是应用内部的资源泄漏或者线程死锁/阻塞,而且结合你说的没做过部署重启,周期性Bug的可能性很大。下面给你一套从易到难的排查步骤,一步步来:
1. 先抓核心日志,定位异常触发点
日志是排查这类问题的第一手资料,别跳过:
- 优先看Tomcat的
catalina.out或localhost.log,重点找故障时间点的错误堆栈、线程阻塞告警(比如Timeout waiting for idle object这种连接池超时提示) - 检查该WebApp自己的业务日志(如果有单独配置的话),看请求超时前有没有异常输出——比如数据库查询卡住、第三方API调用超时没释放连接
- 翻Tomcat的
localhost_access_log.*日志,定位触发超时的具体请求路径,看看是不是某个特定接口搞的鬼
2. 分析线程状态(最关键的一步!)
重启WebApp没用但重启Tomcat能好,很大概率是Tomcat的全局线程池里绑定了该应用的阻塞线程,或者应用自己的线程池彻底耗尽了:
- 问题复现时,立刻用
jstack <Tomcat的PID>导出全量线程栈,搜索你应用的包名前缀(比如com.yourcompany.yourapp),找处于BLOCKED、WAITING状态的线程,尤其是那些持有锁但一直没释放的 - 对比应用正常运行时的线程栈,找出异常线程的完整调用链——比如是不是某个数据库连接没关闭,或者出现了锁竞争导致的死循环
- 嫌jstack输出太啰嗦?可以用
jcmd <TomcatPID> Thread.print快速查看线程状态,输出更简洁
3. 排查资源泄漏问题
虽然硬件利用率看起来都不高,但应用自己的资源池可能已经被榨干了:
- 用
jmap -histo:live <TomcatPID>查看当前存活的对象,重点盯数据库连接对象(比如Connection、PreparedStatement)、线程池相关对象的数量,对比正常运行时是不是异常增长 - 检查应用的连接池配置:比如最大连接数是不是设得太小?有没有开启超时回收(比如Tomcat JDBC池的
removeAbandonedTimeout参数) - 观察内存变化趋势:当前内存68%看似正常,但可能是老年代内存泄漏——用
jstat -gc <TomcatPID> 1000 10每隔1秒输出一次GC状态,如果Full GC频繁触发但内存没降下来,赶紧做堆转储(jmap -dump:format=b,file=heap.hprof <TomcatPID>)分析
4. 排查Tomcat容器级的应用隔离问题
同一实例其他应用正常,说明不是全局故障,但也要确认几个点:
- 检查该WebApp的
context.xml配置,有没有和其他应用共享全局资源(比如全局数据源)?如果共享的话,可能其他应用占用了资源导致该应用拿不到 - 看Tomcat
conf/server.xml里的Connector配置,是不是给这个应用分配了独立的线程池?如果线程池的最大线程数太小,也会导致请求排队超时 - 排查类加载冲突:该应用有没有引入和Tomcat容器冲突的jar包(比如Servlet API版本不一致)?类加载异常有时候会引发奇怪的阻塞问题
5. 模拟周期性触发场景
既然怀疑是周期性Bug,得想办法复现它:
- 检查应用内部的定时任务(比如Quartz、Spring Task),看看故障发生前有没有执行批量数据处理、缓存刷新这类任务——这类任务很容易占用资源不释放
- 用压测工具(比如JMeter)模拟高并发请求该应用的核心接口,看是不是流量高峰时触发问题
- 顺便看看系统层面的周期性任务(比如Linux的cron),有没有在故障时间点执行备份、磁盘清理这类操作,会不会间接影响应用
临时缓解措施(先止损)
在彻底找到根因前,先做这些操作减少故障影响:
- 给该WebApp配置Tomcat的自动重启:在
context.xml里添加监控配置,或者用监控工具(比如Prometheus+Alertmanager)触发自动重启 - 调整应用连接池参数,开启
removeAbandoned="true"强制回收泄漏的连接,避免连接池耗尽
内容的提问来源于stack exchange,提问作者Gabriel Fonseca
相关产品推荐
相关产品推荐

