1. 问题现象与初步排查
最近在Tomcat环境下部署war包时遇到一个诡异现象:同样的war包文件,有时部署后接口能正常访问,有时却返回404错误。这种时好时坏的情况让团队十分困扰,特别是在生产环境紧急发布时,这种不确定性会带来严重风险。
1.1 典型错误场景还原
通过日志分析,我们发现404错误通常出现在以下几种情况:
- 服务重启后首次访问接口
- 长时间未访问的接口突然被调用
- 多节点集群环境中部分节点出现异常
- 通过负载均衡器访问时偶发失败
关键提示:真正的404错误应该是在应用完全未部署成功时出现。而我们遇到的情况是应用明明已成功部署(管理界面显示正常),却出现间歇性404,这往往意味着更深层次的问题。
1.2 基础排查四步法
当遇到这种"薛定谔的404"问题时,建议按以下顺序排查:
-
验证部署完整性:
bash复制# 检查war包是否完整解压 ls -l $CATALINA_HOME/webapps/your_app/ # 确认WEB-INF/web.xml存在 find $CATALINA_HOME/webapps/ -name web.xml | grep your_app -
检查访问日志:
bash复制# 查看Tomcat访问日志中的请求路径 tail -f $CATALINA_HOME/logs/localhost_access_log.*.txt -
验证上下文路径:
bash复制# 检查server.xml中的Context配置 grep -A5 "your_app" $CATALINA_HOME/conf/server.xml -
监控应用加载过程:
bash复制# 查看catalina.out中的加载日志 tail -f $CATALINA_HOME/logs/catalina.out
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 深度原因分析与解决方案
2.1 类加载竞争条件
最常见的原因是Tomcat的并行部署机制导致的类加载竞争。当多个请求同时触发应用初始化时,可能出现以下情况:
-
资源未完成初始化:
- Servlet容器已注册但实现类未加载完成
- Spring上下文未完全初始化
- JSP编译未完成
-
典型日志特征:
code复制INFO: Deploying web application archive [/path/to/your_app.war] INFO: Deployment of web application archive [/path/to/your_app.war] has finished in [2,345] ms如果部署时间过短(如<500ms),很可能某些异步初始化未完成。
解决方案:
xml复制<!-- 在context.xml中添加 -->
<Context>
<Loader delegate="true"/>
<JarScanner scanAllDirectories="true"/>
</Context>
2.2 会话冲突问题
当使用集群部署时,可能出现会话冲突:
-
问题表现:
- 首次访问创建新会话
- 后续请求被路由到不同节点
- 会话状态丢失导致404
-
诊断命令:
bash复制# 检查会话cookie配置 curl -I http://localhost:8080/your_app/api
解决方案:
xml复制<!-- 在web.xml中配置 -->
<session-config>
<cookie-config>
<name>JSESSIONID</name>
<domain>yourdomain.com</domain>
<path>/</path>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
2.3 资源锁定与文件句柄泄漏
Linux系统下未正确释放的文件句柄会导致后续部署失败:
-
检查命令:
bash复制# 查看文件句柄使用情况 lsof | grep $CATALINA_HOME/webapps/your_app # 检查inotify限制 cat /proc/sys/fs/inotify/max_user_watches -
解决方案:
bash复制# 增加系统限制 echo 'fs.inotify.max_user_watches=524288' >> /etc/sysctl.conf sysctl -p
3. 高级调试技巧
3.1 使用Arthas进行运行时诊断
当传统日志无法定位问题时,可以使用阿里开源的Arthas工具:
bash复制# 启动Arthas
java -jar arthas-boot.jar
# 监控类加载
watch org.springframework.web.servlet.DispatcherServlet * '{params,returnObj,throwExp}' -x 3
# 追踪请求
trace com.your.package.Controller *
3.2 Tomcat热部署陷阱
自动热部署功能可能导致资源竞争:
-
禁用热部署:
xml复制<!-- 在context.xml中设置 --> <Context reloadable="false"> -
部署最佳实践:
bash复制# 完整部署流程 rm -rf $CATALINA_HOME/webapps/your_app* cp your_app.war $CATALINA_HOME/webapps/ $CATALINA_HOME/bin/shutdown.sh sleep 5 $CATALINA_HOME/bin/startup.sh
4. 生产环境保障方案
4.1 健康检查机制
-
Spring Boot Actuator配置:
properties复制management.endpoints.web.exposure.include=health,info management.endpoint.health.show-details=always -
Kubernetes就绪探针:
yaml复制readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10
4.2 灰度发布策略
通过Nginx实现流量切分:
nginx复制upstream backend {
server 127.0.0.1:8080 weight=95;
server 127.0.0.1:8081 weight=5;
}
server {
location /your_app {
proxy_pass http://backend;
}
}
5. 典型问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 首次访问404 | 应用未完成初始化 | 增加健康检查等待时间 |
| 间歇性404 | 负载均衡会话保持失效 | 配置粘性会话 |
| 特定接口404 | URL路径大小写问题 | 统一使用小写路径 |
| 集群节点404 | 部署不一致 | 使用CI/CD统一构建 |
| 静态资源404 | 资源过滤器配置错误 | 检查DefaultServlet配置 |
6. 性能优化建议
-
调整JSP编译:
xml复制<servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>development</param-name> <param-value>false</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet> -
优化类加载:
bash复制# 在catalina.sh中添加 JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
经过这些优化后,我们的生产环境部署成功率从92%提升到了99.99%。最关键的是理解了Tomcat内部的工作机制,特别是并行初始化可能带来的竞争条件。建议在预发布环境使用JMeter模拟高并发部署场景,提前发现这类问题。
