ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Tomcat从安装到生产级配置:版本、server.xml与故障排查实战

Tomcat从安装到生产级配置:版本、server.xml与故障排查实战 1. 装之前先对版本Tomcat九代十代的坑就藏在这里先分享一个我见过无数次的场景新人从网上下了一个Tomcat双击startup.bat窗口一闪而过怎么也起不来。第一反应是百度“Tomcat启动闪退”翻了几页试了各种方法最后才发现是下载的Tomcat版本和本机JDK对不上。这种问题看着简单但每次都会带走一个下午。Tomcat的版本对应关系其实从官网的README和发布说明里都写得很清楚但没有几个人耐着性子看。我给一张我反复验证过的对照表这张表基本覆盖了现在还在用的版本Tomcat版本Servlet规范JSP规范最低JDK默认包名Tomcat 8.5.xServlet 3.1JSP 2.3JDK 7javax.*Tomcat 9.0.xServlet 4.0JSP 2.3JDK 8javax.*Tomcat 10.0.xServlet 5.0JSP 3.0JDK 8jakarta.*Tomcat 10.1.xServlet 5.0JSP 3.1JDK 11jakarta.*Tomcat 11.0.xServlet 6.0JSP 4.0JDK 17jakarta.*这张表里最坑的就是10.0这个分水岭。很多老项目用的还是javax.servlet如果你拿Tomcat 10去部署编译直接报ClassNotFoundException: javax.servlet.Filter。因为从10.0开始Java EE正式移交给了Eclipse基金会命名空间从javax整体换成了jakarta。这可不只是改个字母的事所有依赖Servlet API的框架、Filter、Listener全部要换import。我见过有人把Spring Boot打好的war包丢进Tomcat 10结果白屏加500查了一下午才发现是命名空间的问题。我个人在给生产环境选型时只要项目还是老代码无脑用9.0.x新项目、新框架直接上10.1.x或11。如果是学习入门、跑老教程8.5和9也没问题。风险最大的反而是“下载最新版”这个思路——最新版对JDK的要求往往比你本机高出不少装完就跑不起来。下载渠道方面我建议优先去Apache官网找tar.gz或zip版本而不是Windows Installer。官网首页右侧有镜像列表国内用户选清华或阿里云镜像速度快很多。另外注意区分Core、Full documentation和Deployer我们要的就是Core里的那一个不要手滑下成源码包。2. 目录作用与运行配置装完并不等于能用Tomcat解压完就得到一个类似下面这样的目录结构。很多人直接忽略这些文件夹出了问题就不知道去哪看日志这里花三分钟过一遍bin启动和关闭脚本Linux用startup.sh/shutdown.shWindows用startup.bat/shutdown.bat。conf所有配置文件都在这核心是server.xml、web.xml、context.xml、logging.properties。libTomcat运行所需的jar包。注意这里不是给你放项目依赖的地方放错了可能影响整个容器的类加载。logs日志目录。catalina.out是主日志乱码和异常一般先看这个。webapps默认的部署目录。war包丢进来Tomcat会自动解压并发布。workJSP编译后的class文件、临时文件所在目录。你改了JSP不生效时可以来这清缓存。temp运行时临时文件。很多人把解压当成安装完成其实还差两步关键配置。第一确认JAVA_HOME环境变量。Tomcat的启动脚本不关心你在命令行里能不能敲java -version它只认JAVA_HOME。Windows下设置方式是在“系统变量”里新建JAVA_HOME指向JDK的根目录比如C:\Program Files\Java\jdk-17注意不要指到bin这一层。Linux下可以在/etc/profile里加导出行也可以直接在catalina.sh顶部硬编码export JAVA_HOME/opt/jdk-17 export CATALINA_HOME/opt/tomcat第二设置CATALINA_HOME或CATALINA_BASE。这个变量在单实例部署时一般不强制但你会在某些脚本里频繁依赖它。我习惯把它配上后续写启停脚本、做systemd服务都方便。2.1 为什么我不推荐用exe安装版Windows下很多人倾向下载那个.exe安装包我的建议是除非你要跑成Windows服务否则直接跳过。exe版会把Tomcat装进系统服务管理器启动方式变成了net start Tomcat9日志和配置的位置也被分散到不同目录排查问题时要多绕几个弯。尤其是IDEA关联Tomcat时exe版本经常出现权限和路径空格问题而zip版解压即用、删掉即卸载干净利落。另外还有一点exe版安装时默认会带入64位和32位的选择。Tomcat本身是纯Java程序并不需要区分CPU位数真正区分位数的是它调用的本地库比如某些JDBC驱动、JNI库。如果你要装32位的JDK对应的Tomcat相关依赖也要注意一致不然启动时会报UnsatisfiedLinkError。2.2 三处启动前要改的默认配置装好后直接启动Tomcat默认监听8080端口。但有三个默认配置我建议在跑业务前就改掉关闭端口8005server.xml里默认有Server port8005 shutdownSHUTDOWN任何人只要能访问到8005端口发送字符串SHUTDOWN就能把Tomcat关掉。生产环境必须改掉这个端口和关闭密码。管理端页面默认webapps目录下自带ROOT、docs、examples、manager、host-manager这些应用。个人学习无所谓生产交付时全部删掉只留自己的应用。日志级别默认logging.properties里INFO级别的日志太多尤其在压测时会产生大量无关日志可以按需调成WARNING。3. server.xml精讲端口、连接器、虚拟主机和HTTPS双向认证很多人把server.xml当成一个永远不需要碰的文件。但实际工作中端口冲突、域名配置、HTTPS改造、性能调优全都要落在这份文件上。这个文件的结构其实不复杂核心层级是Server-Service-Engine-Host-Context。你打开文件看到的顶层元素就这几个。3.1 三个要命的端口一个默认Tomcat有三个端口8005管理端口、8080HTTP连接器、8009AJP连接器。先弄清楚分别干什么再谈修改。8080是用户访问Web应用的入口也是最常改的端口。如果8080被占用启动时会报Address already in use。修改方式是在Connector节点上改port属性。8005是管理生命周期端口通过发送SHUTDOWN指令来关闭容器。生产环境建议改成一个不常见端口或者用防火墙直接封掉它。8009用于AJP协议老派架构里用来和Apache httpd做集成。现在是2025年了AJP用得越来越少而且它曾经曝出过GhostcatCVE-2020-1938这样的远程文件读取漏洞。如果你不用AJP最好的处理就是直接注释掉这个Connector。!-- 修改端口示例 -- Connector port9000 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 / !-- 禁用AJP -- !-- Connector port8009 protocolAJP/1.3 redirectPort8443 / --3.2 Connector调优Connector里的参数对并发能力影响很大。我贴一个生产常用的配置模板Connector port8080 protocolorg.apache.coyote.http11.Http11NioProtocol maxThreads400 acceptCount200 minSpareThreads50 connectionTimeout20000 maxConnections10000 keepAliveTimeout60000 maxKeepAliveRequests100 compressionon compressionMinSize2048 noCompressionUserAgentsgozilla, traviata compressibleMimeTypetext/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json URIEncodingUTF-8 /几个参数的逻辑maxThreads控制最大工作线程数默认只有200。如果并发超过这个数请求就会排队表现为响应变慢。但线程数不是越大越好线程越多CPU上下文切换成本越高。我建议从200起步压测后逐档往上涨涨到响应时间不再下降为止。acceptCount是等待队列长度排队的请求超过这个数就直接拒绝。提交给客户端的感觉就是“连接被重置”。compression开启后能明显减少传输体积特别是前端资源多的情况下压缩完能省一半带宽。代价是CPU多了一些负担局域网内无所谓公网环境强烈建议开。URIEncodingUTF-8防止GET请求参数中文乱码。很多老项目乱码就是漏了这个。3.3 Host虚拟主机在Engine节点里可以配置多个Host模拟多域名。比如一台服务器要同时跑www.a.com和www.b.com就配置两个HostEngine nameCatalina defaultHostwww.a.com Host namewww.a.com appBasewebapps_a unpackWARstrue autoDeployfalse Aliasa.com/Alias /Host Host namewww.b.com appBasewebapps_b unpackWARstrue autoDeployfalse Aliasb.com/Alias /Host /Engine这里有个细节appBase是应用部署目录可以分别指定两个Host互不干扰。autoDeploy建议在开发环境开生产环境关掉避免动态部署引起意外重启。3.4 HTTPS双向认证配置实战热词里有人问“SSL双向认证Tomcat下如何配置”这个场景通常在金融、政务内外网对接时遇到。简单解释单向认证是客户端验证服务器双向认证是客户端和服务器互相验证证书。在Tomcat里做双向认证步骤大致如下用keytool生成服务器密钥对并导出证书。生成客户端证书并导入服务器的信任库。修改server.xml的Connector加上clientAuthtrue。# 生成服务器密钥对示例 keytool -genkeypair -alias server -keyalg RSA -keysize 2048 -keystore server.keystore.jks -validity 3650 # 导出服务器证书 keytool -exportcert -alias server -keystore server.keystore.jks -file server.cer # 生成客户端密钥对 keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -keystore client.keystore.jks -validity 3650 # 导出客户端证书 keytool -exportcert -alias client -keystore client.keystore.jks -file client.cer # 把客户端证书导入服务器的信任库 keytool -importcert -alias client -file client.cer -keystore server.truststore.jks然后修改ConnectorConnector port8443 protocolorg.apache.coyote.http11.Http11NioProtocol SSLEnabledtrue maxThreads150 schemehttps securetrue clientAuthtrue keystoreFileconf/server.keystore.jks keystorePasschangeit truststoreFileconf/server.truststore.jks truststorePasschangeit /我踩过的坑是clientAuthtrue和truststoreFile必须配对。如果你只配置了keystoreFile没有配truststoreFile启动会报找不到信任库因为服务器根本不知道该拿哪个库去验证客户端的证书。另外证书的CNCommon Name必须和访问域名一致否则浏览器那关过不去。4. 部署Web项目的几种方式与IDEA集成实战部署是日常频率最高的操作。我把实战中最常用的几种方式都过一遍并说清楚各自适合什么场景。4.1 war包部署直接扔进webapps打包成war后扔到webapps目录下启动时会自动解压并部署。这种方式最直观也适合静态目录发布。# 打包命令在项目根目录执行 mvn clean package -DskipTests # 将生成的xxx.war复制到webapps目录 cp target/xxx.war /opt/tomcat/webapps/注意一点war包的文件名就是你的应用访问路径。比如你叫demo.war访问URL就是http://localhost:8080/demo/。要想直接通过根路径访问得把war包改名为ROOT.war或者删掉原来的ROOT目录再部署。4.2 IDEA配置本地TomcatIDEA里配置Tomcat是初学者的高频问题。2025年的IDEA版本虽然界面有一些小变化但核心逻辑没变IDEA不是帮你装Tomcat而是“关联”一个你本机已有的Tomcat。步骤拆解打开File - Settings - Build, Execution, Deployment - Application Servers点选Tomcat Server指定Tomcat安装目录。在Project Structure - Project里确认SDK也就是JDK版本必须和Tomcat要求的版本匹配否则后面跑起来会报UnsupportedClassVersionError。创建一个Run/Debug Configuration在Tomcat Server下选Local再在Deployment选项卡里添加Artifact选Web项目的war exploded。Server选项卡里改HTTP port和JMX portJMX端口默认1099和8080冲突的情况我遇到过改了就好。IDEA里有两种Artifactwar和war exploded前者是打包好的压缩包后者是解压后的目录相当于“展开模式”。开发阶段建议用war exploded修改代码后热重载速度更快IDEA会自动增量更新到Tomcat的部署目录不需要每次手动打包。我第一次用IDEA跑Tomcat时老是不明白为什么改了代码不生效后来发现是因为选了war每次都要重新build。4.3 数据库连接配置加密热词里有人问“Tomcat数据库配置加密”。Tomcat的context.xml里配置数据源时用户名密码是明文。一旦服务器文件泄露数据库就等于裸奔。常见做法是引入Jasypt一类库把数据库密码加密后再放到配置里启动时用环境变量或启动参数传密钥解密。context.xml里配置数据库源的思路大概是这样Context Resource namejdbc/mydb authContainer typejavax.sql.DataSource driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/mydb?useUnicodetrueamp;characterEncodingUTF-8 usernameENC(SM9Ws...) passwordENC(Kc2aP...) / /Context结合Jasypt的做法是在catalina.properties或JVM参数里指定jasypt.encryptor.password然后在Resource的username和password里写密文运行时由Jasypt的解析器统一解密。这里我就不贴完整代码了核心是想提醒不要把明文密码写在服务器配置文件里这个坑一旦踩到就是生产事故。4.4 Spring Boot项目的内嵌容器替换热词里还出现了“Spring Boot如何最小改造使用内嵌宝兰德替换Tomcat”“Tomcat国产替代方案”。这个话题这两年确实问得多核心逻辑其实不复杂Spring Boot默认用内嵌Tomcat作Web容器如果你要换成另一个Web容器要做的事就是“排除Tomcat依赖引入新容器的依赖”剩下的业务代码完全不用动。Maven里排除Tomcat引入某替代容器的写法大致是这样的思路dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency dependency groupIdcom.example/groupId artifactIdsome-container-starter/artifactId /dependency替换容器的动机很多有的是合规需求有的是性能需求有的是摆脱国外基础软件依赖。无论出于什么目的最小改造原则都一样只换容器层不碰业务代码。这样验证成本最低回滚也最快。5. 故障排查手册乱码、启动失败、安全漏洞怎么处理最后这部分是实战中大多数人真正会遇到的坑。我按出现频率从高到低梳理了一遍。5.1 日志和控制台乱码Tomcat乱码几乎是必踩的坑而且有几个不同位置的乱码处理方式完全不同。控制台启动日志乱码Windows下最常见。Tomcat的日志使用了UTF-8而Windows控制台默认编码是GBK。解决方案是在logging.properties里把控制台handler的编码改成GBK或者给启动脚本加上-Dfile.encodingUTF-8。应用返回内容乱码先看server.xml里Connector是否配置了URIEncodingUTF-8再看响应头里Content-Type是否带charsetutf-8最后看页面文件本身保存的编码格式。这三个环节只要有一个不对页面就乱。JSP页面输出乱码在JSP文件头部写% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%这个错误通常来自老项目文件本身是GBK的强行在页面里设UTF-8结果还是会乱。work目录下看不到JSP编译后的.java源码这个问题也常被问。Tomcat默认在启动时把JSP编译成Java文件再编成class如果编译失败或目录被清理就看不到源文件。想保留编译产生的Java文件可以在web.xml里的JspServlet参数中设置scratchdir指向一个固定目录编译产物就会留在这个目录里排查问题会方便很多。5.2 启动失败的完整排查链路Tomcat启动失败的现象很多但排查路径是固定的。我的习惯是先把错误定位到日志再看端口再看JVM。第一步看logs/catalina.out。这个文件记录了容器本身的启动过程任何异常都会出现在这里。很多人启动失败后上来就改配置这是错的应该先找到异常堆栈的第一行。第二步查端口占用。启动报Address already in use时排查命令是# Linux下 ps -ef | grep java netstat -tlnp | grep 8080 # Windows下 netstat -ano | findstr 8080 tasklist | findstr 端口对应的PID第三步看JVM和JDK。启动报UnsupportedClassVersionError说明你编译用的JDK版本比Tomcat运行时用的更高报Could not reserve enough space for object heap说明CATALINA_OPTS里写的-Xmx超过了机器物理内存报could not obtain connection to query metadata那问题基本不在Tomcat而在数据源——数据库连接失败、JDBC驱动缺失或连接池满了。could not obtain connection这个报错我多说两句。它经常出现在IDEA里启动Tomcat访问项目时表面看像是Tomcat起了问题实际上错误栈里跟着的是Cannot create PoolableConnectionFactory或者Communication link failure这类信息。这时候应该去查数据库服务是否正常、连接账号密码是否正确、context.xml里的JDBC URL是否可达。我曾经花了一下午调Tomcat参数最后发现是MySQL服务没启动。5.3 远程命令执行漏洞与安全加固Tomcat几乎每年都会曝出CVE漏洞最出名的是GhostcatCVE-2020-1938和后续几个反序列化、请求走私漏洞。应对思路不是去背漏洞编号而是建立一套固定的加固清单升级到安全版本。Apache会及时修复老版本被公开利用的工具一堆。至少保证小版本号是最新的。禁用不必要的连接器。不用AJP就把8009注释掉Ghostcat就是利用AJP协议攻击的。删除自带Web应用。webapps下只保留自己的应用manager和host-manager如果不做远程管理一律删掉。修改默认端口。8080、8005、8009这些默认端口在扫描工具面前等于告诉别人“我是Tomcat”改成非常用端口能挡住一部分无差别攻击。最小化权限。Tomcat运行用户不要用root给一个无shell的系统账户只授予CATALINA_HOME和日志目录的读写权限。网络隔离。8005端口只允许本机访问外部一律防火墙丢弃。这条清单不是做完一次就结束的。每次Tomcat发布新版本后去官网看一眼安全公告页把涉及自己版本的漏洞对照一遍这是最省事的做法。5.4 一个被忽略的小细节启动慢有段时间我总觉得Tomcat启动特别慢卡在“Initializing Spring”附近要等很久。查了一圈发现是Linux熵池不足Tomcat启动时要初始化安全机制需要从/dev/random读取随机数而虚拟机环境熵不够启动就阻塞。解决方案很简单在catalina.sh的JVM参数里加一行CATALINA_OPTS$CATALINA_OPTS -Djava.security.egdfile:/dev/./urandom这个改动在开发环境立竿见影。有意思的是这个参数加完后很多人会困惑/dev/urandom和/dev/random有什么本质区别简单说/dev/random在熵不足时会阻塞/dev/urandom不会密码学强度在绝大多数Web场景下都够用。但如果你在跑对随机性要求极高的加密服务这个参数还是得谨慎评估。我在实际项目中还遇到过一个更隐蔽的情况IDEA关联Tomcat后每次启动都用Debug模式速度比普通启动慢好几倍。那个不叫慢是JVM在等调试器挂载。搞清楚这个区别之后排查方向就不会跑偏。Tomcat这个东西说复杂不复杂说简单也不简单。我用了十几年最大的体会是大部分问题都不是Tomcat本身的问题而是版本不匹配、配置一知半解、日志没有沉下心看。你把上面这些基础配置和排查链路都走一遍至少能解决掉百分之八十以上的日常故障。
返回列表