Docker容器中Tomcat运行时Servlet加载两次问题求助
从你提供的堆栈跟踪能明显看到两次Servlet加载的触发路径差异:
- 启动阶段:由
StandardContext.loadOnStartup触发,这完全符合你在web.xml里配置load-on-startup:1的预加载预期 - 首次请求阶段:由
StandardWrapper.allocate触发,这说明Tomcat此时需要重新创建Servlet实例——意味着之前的实例已经被销毁,或者整个Web上下文被重新加载了
本地Tomcat正常但Docker环境异常,大概率是Docker镜像中的Tomcat默认配置和本地存在差异,导致了上下文重复部署/自动重新加载的问题。
1. Tomcat自动部署特性触发重复部署
Docker官方的tomcat:8.5镜像默认同时开启了autoDeploy和deployOnStartup。容器启动时,Tomcat会先通过deployOnStartup部署ROOT.war,之后autoDeploy扫描目录时,可能会误判文件状态再次触发部署(即使WAR包本身没有变化)。
解决方法:
修改Tomcat的conf/server.xml中<Host>元素的配置,关闭自动部署:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="false" deployOnStartup="true">
你可以在本地提前修改好server.xml,然后通过Dockerfile替换镜像中的默认配置:
FROM tomcat:8.5 RUN rm -rf /usr/local/tomcat/webapps/* # 复制自定义的server.xml到镜像中 COPY conf/server.xml /usr/local/tomcat/conf/server.xml ADD target/my-webapp.war /usr/local/tomcat/webapps/ROOT.war ENV JPDA_ADDRESS="8000" ENV JPDA_TRANSPORT="dt_socket" ENV CATALINA_OPTS="-Dcom.sun.management.jmxremote=true \ -Dcom.sun.management.jmxremote.port=8086 \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false" CMD ["catalina.sh", "jpda", "run"]
2. Context的reloadable属性导致自动重新加载
如果你的应用上下文配置了reloadable="true",Tomcat会持续监控WEB-INF目录下的文件变化,一旦检测到疑似变化就会重新加载上下文,进而导致Servlet被再次初始化。
解决方法:
检查conf/context.xml或者应用自身的META-INF/context.xml,确保reloadable设置为false:
<Context reloadable="false">
同样可以通过Dockerfile将修改后的配置文件复制到镜像中。
3. 验证方法
启动容器后,查看Tomcat的catalina.out日志,如果能看到类似Context [] started和Context [] reloaded的日志,就可以100%确认是上下文重新加载导致的Servlet重复初始化问题。
内容的提问来源于stack exchange,提问作者Jobs

