找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3840

积分

0

好友

532

主题
发表于 13 小时前 | 查看: 0| 回复: 0

当你在启动 Tomcat 服务时,发现进程看似启动却无法正常提供服务,翻遍日志文件又找不到任何明显的错误信息,这种“沉默的失败”无疑让人头疼。本文将通过一个实际案例,分享三个行之有效的排查思路,帮助你快速定位并解决这类问题。

问题现象与日志分析

以下是典型的场景:执行启动脚本后,catalina.out 日志打印了一些初始化信息,然后就停滞了。

[root@m001 logs]# /data/tomcat/crm/bin/startup.sh

[root@m001 logs]# tail -f catalina.out

26-Feb-2026 15:59:44.338 信息 [main] org.apache.catalina.startup.Catalina.load 服务器在[443]毫秒内初始化

26-Feb-2026 15:59:44.356 信息 [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]

26-Feb-2026 15:59:44.357 信息 [main] org.apache.catalina.core.StandardEngine.startInternal 正在启动 Servlet 引擎:[Apache Tomcat/9.0.31]

26-Feb-2026 15:59:44.369 信息 [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/data/tomcat/crm/webapps/datahubServer.war]

26-Feb-2026 15:59:44.729 信息 [main] org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。

SLF4J: Class path contains multiple SLF4J bindings.

SLF4J: Found binding in [jar:file:/data/tomcat/shared-libs/log4j-slf4j-impl-2.12.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: Found binding in [jar:file:/data/tomcat/shared-libs/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]

SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.

SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

从日志看,Tomcat 容器本身的初始化是成功的,但随后便卡在了部署应用 datahubServer.war 的阶段。日志最后停留在 SLF4J 绑定声明,这是一个关键信号:日志框架已经完成了初始化并接管了后续输出。程序没有崩溃,也没有抛出异常,那它究竟在做什么呢?通常,此时应用正处在 Spring 框架或业务代码的初始化 过程中,可能因为某些资源无法获取或执行了耗时操作而“卡住”。

面对这种局面,可以按照以下三种思路进行系统性 排查

思路一:使用 jstack 强制打印线程堆栈(最快定位工具)

既然程序不声不响地停在那里,最直接的方法就是看看它内部所有线程当前正在执行什么代码。jstackJava 开发者的“手术刀”,能够立刻揭示进程的内部状态。

操作步骤

  1. 找到 Tomcat 进程的 PID (进程 ID):
    ps -ef | grep tomcat
  2. 使用 jstack 命令导出所有线程的堆栈信息:
    jstack <PID> > /tmp/stack.txt

分析技巧
打开生成的 /tmp/stack.txt 文件,重点查看那些处于 RUNNABLE (正在运行) 或 WAITING/TIMED_WAITING (等待) 状态的线程。

  • 等待网络I/O:如果看到大量线程卡在 SocketInputStream.read 或类似调用上,很可能是在等待数据库连接或远程接口响应。
  • 熵池阻塞:如果发现线程在 SecureRandom 相关方法上等待,这是 Linux 新服务器上一个经典问题,因随机数熵池不足导致。
  • 数据库连接问题:线程可能卡在数据库连接池获取连接的地方。

实战案例
在本次问题中,执行 jstack 后,发现了明确的线索。

jstack pid >/tmp/stack.txt

分析堆栈文件,一眼就能看到有线程正在等待 Oracle 数据库连接池的资源,状态是 TIMED_WAITING

Java线程堆栈分析截图,显示Oracle JDBC驱动线程在等待连接资源

根据这个线索,立即检查 Oracle 数据库状态,发现数据库实例已关闭,并且系统日志 (/var/log/messages) 中存在因内存不足 (OOM) 而终止进程的记录。

系统日志显示Out of Memory错误信息

问题根源就此锁定:后端数据库不可用导致应用启动时初始化数据库连接池超时、挂起。

思路二:解决日志框架冲突(SLF4J Multiple Bindings)

回过头看最初的日志,其实已经给出了一个警告:Class path contains multiple SLF4J bindings。这表明类路径中包含了多个 SLF4J 的实现绑定(如 log4j-slf4j-implslf4j-log4j12)。

这种冲突可能导致

  1. 日志输出行为不可预测,真正的错误日志可能被重定向到某个不为人知的文件,甚至被“吞掉”。
  2. 在极端情况下,可能引发类加载死锁,导致应用卡在初始化阶段。

解决方案
进入日志中提示的目录(例如 /data/tomcat/shared-libs/),移除或重命名其中一个冲突的 JAR 包。通常建议保留更新、更主流的绑定(如 Log4j2 的 log4j-slf4j-impl),移除旧的绑定(如 slf4j-log4j12-1.7.30.jar)。

cd /data/tomcat/shared-libs/
mv slf4j-log4j12-1.7.30.jar slf4j-log4j12-1.7.30.jar.bak

然后重启 Tomcat,观察 catalina.out 是否能有更清晰的错误输出。

思路三:调整应用内部日志级别

Tomcat 的 logging.properties 只控制 Tomcat 自身的日志级别。你的 Web 应用(如 datahubServer.war)通常使用独立的日志框架,如 Log4j2 或 Logback。

操作步骤

  1. 找到配置文件:解压或进入应用目录,查找日志配置文件。
    # 通常路径如下
    /data/tomcat/crm/webapps/datahubServer/WEB-INF/classes/log4j2.xml
    或
    /data/tomcat/crm/webapps/datahubServer/WEB-INF/classes/logback.xml
  2. 修改日志级别:将根日志级别从 INFO 改为 DEBUG,以便在初始化阶段打印更详细的信息。
    <!-- 以Log4j2为例 -->
    <Root level="DEBUG"> <!-- 原来是 INFO -->
        <AppenderRef ref="Console"/>
        <AppenderRef ref="File"/>
    </Root>
  3. 检查日志路径:仔细查看配置文件中配置的 FileRollingFile Appender 的路径。很可能错误日志被输出到了应用专属的日志文件(如 logs/app.log),而不是 Tomcat 的 catalina.out。修改级别后,去正确的日志文件里寻找线索。

其他常见“卡死”原因速查

结合 jstack 和日志调整,你还可以快速排查以下高频问题:

  1. 数据库/远程服务连接超时:检查应用配置(如 application.propertiesspring 配置文件)中的数据库连接串、用户名密码、网络可达性。
  2. Linux熵池不足导致SecureRandom阻塞
    如果 jstack 显示大量线程卡在 java.security.SecureRandom 相关方法,可在 tomcat/bin/catalina.shJAVA_OPTS 中添加参数解决:
    JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
  3. 内存溢出(OOM)但未生成日志:应用可能被操作系统直接终止。
    • 检查系统日志:dmesg | grep -i killgrep -i oom /var/log/messages
    • 检查 Tomcat 日志目录下是否有 Java 生成的堆转储文件(java_pidXXXX.hprof)。

通过以上“三板斧”——线程堆栈分析、日志冲突清理、日志级别调整,你就能系统性地攻克 Tomcat “启动不成功也不报错”的疑难杂症。在实际运维中,养成结合多种工具进行排查的习惯至关重要。希望这个案例能为你提供清晰的解决路径。欢迎在 云栈社区 分享你在实践中遇到的其他有趣案例或问题。




上一篇:Claude远程控制来了,用手机编程是真方便还是“伪需求”?
下一篇:教程:免费开通谷歌Gemini Pro,学生资格与Coursera课程两种方法详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-27 18:46 , Processed in 1.519710 second(s), 47 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表