结论: Tomcat 部署项目的标准流程是:装 JDK(Tomcat 10 需 JDK 11+,Tomcat 11 需 JDK 17+)→ 下载解压 Tomcat → 配置 server.xml(端口、连接器、Host)→ 配置 setenv.sh 设置 JVM 内存 → 用 systemd 托管 → 打 WAR 包放到 webapps 或用 manager 部署 → 前面挂 Nginx 反代 80/443。生产环境务必改掉默认管理密码、关闭 autoDeploy 热部署、并调 JVM 堆参数。
引言:Tomcat 到底在架构里扮演什么角色
很多人第一次接触 Tomcat 会把它当成"和 Nginx 一样的东西",其实两者层次不同。Tomcat 是 Servlet 容器(Servlet Container),也是轻量 Web 服务器:它负责加载 Java Web 应用(WAR 包),管理 Servlet 生命周期,处理 JSP 编译和 HTTP 请求;而 Nginx 是纯粹的 Web 服务器/反向代理,不执行 Java 代码。
所以典型架构是:客户端 → Nginx(80/443,SSL、静态资源、限流)→ Tomcat(8080,跑 Java 应用)。Tomcat 自己也能直接对外提供 80 端口服务,但在静态资源性能、SSL、负载均衡上不如 Nginx,生产环境几乎都会前置一层。
本篇从零开始,按实际部署顺序讲:JDK → Tomcat → 配置 → JVM → 部署 → 反代 → 排错。
一、JDK 安装:版本必须和 Tomcat 版本匹配
Tomcat 版本与 JDK 版本是强绑定关系,装错 JDK 会在启动时直接报 UnsupportedClassVersionError。
| Tomcat 版本 | 最低 JDK | Servlet/JSP 规范 | 说明 |
|---|---|---|---|
| Tomcat 9 | JDK 8 | Servlet 4.0 / JSP 2.3 | 老项目维护,包名仍是 javax.* |
| Tomcat 10.1.x | JDK 11 | Servlet 6.0 / JSP 3.1 | **包名从 javax. 改为 jakarta.**,迁移需注意 |
| Tomcat 11(截至 2026 年为最新主线) | JDK 17 | Servlet 6.1 / JSP 4.0 | 同样使用 jakarta.* 命名空间 |
迁移注意:Tomcat 10 起 javax.servlet. 变成了 jakarta.servlet.,老项目直接部署会报 ClassNotFoundException。Spring Boot 3.x 已适配 jakarta,Spring Boot 2.x 项目请用 Tomcat 9 或用官方迁移工具转换。
安装 OpenJDK(以 Ubuntu 24.04 + JDK 17 为例):
sudo apt update
sudo apt install -y openjdk-17-jdk-headless # headless 版无图形库,服务器推荐
java -version
# openjdk version "17.0.x" 2026-xx-xx
javac -version
# 配置 JAVA_HOME(systemd 服务里会用到)
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' | sudo tee /etc/profile.d/java.sh
source /etc/profile.d/java.sh
echo $JAVA_HOME二、Tomcat 安装与目录结构
官方推荐用二进制包解压安装,不要用系统包管理器装(版本往往滞后且目录被拆散)。
# 创建专用运行用户(绝不用 root 跑 Tomcat)
sudo useradd -r -m -d /opt/tomcat -s /bin/false tomcat
# 下载 Tomcat 11(版本号截至 2026 年请以官网为准)
cd /tmp
wget https://dlcdn.apache.org/tomcat/tomcat-11/v11.0.x/bin/apache-tomcat-11.0.x.tar.gz
sudo mkdir -p /opt/tomcat
sudo tar -xzf apache-tomcat-11.0.x.tar.gz -C /opt/tomcat --strip-components=1
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh目录结构说明:
| 目录 | 作用 |
|---|---|
bin/ | 启动脚本(startup.sh、shutdown.sh、catalina.sh) |
conf/ | 配置文件:server.xml、web.xml、context.xml、tomcat-users.xml |
lib/ | Tomcat 自身与全局类库(数据库驱动可放这里) |
logs/ | catalina.out(标准输出)、localhost_access_log.*.txt(访问日志) |
webapps/ | 应用部署目录,放 WAR 或解压后的目录 |
work/ | JSP 编译后的 Servlet 源码与 class |
temp/ | 临时文件 |
启动与验证:
sudo -u tomcat /opt/tomcat/bin/startup.sh
curl -I http://127.0.0.1:8080/ # 返回 200 说明启动成功
tail -f /opt/tomcat/logs/catalina.out三、server.xml 关键配置
server.xml 是 Tomcat 的主配置,核心是 Service → Connector → Engine → Host 的层次结构。下面给出生产常用的关键配置项说明。
关键参数含义:
| 参数 | 建议值 | 说明 |
|---|---|---|
port | 8080(或改其他) | HTTP 监听端口 |
protocol | HTTP/1.1(默认 NIO) | 高并发可改 org.apache.coyote.http11.Http11Nio2Protocol |
maxThreads | 200~800 | 最大工作线程数,即最大并发请求数 |
acceptCount | 100~500 | 队列长度,满了就拒绝连接 |
maxConnections | 5000~20000 | NIO 模式下可保持的最大连接数 |
connectionTimeout | 20000ms | 连接超时 |
enableLookups | false | 必须关,否则反向 DNS 查询严重拖慢 |
URIEncoding | UTF-8 | 解决 GET 参数中文乱码 |
autoDeploy | false | 生产关闭热部署,避免运行时被替换 |
reloadable | false | 生产关闭类重载(开销大且可能内存泄漏) |
unpackWARs | true | 自动解压 WAR |
四、JVM 参数与 setenv.sh
Tomcat 的默认堆内存很小(通常 1/4 物理内存或更低),生产必须显式设置。配置文件是 bin/setenv.sh(默认不存在,需自建,Tomcat 启动时会自动读取)。
sudo -u tomcat tee /opt/tomcat/bin/setenv.sh <<'EOF'
#!/bin/bash
export CATALINA_HOME=/opt/tomcat
export CATALINA_BASE=/opt/tomcat
export CATALINA_PID=/opt/tomcat/tomcat.pid
# 内存:初始与最大设为相同,避免运行时扩容抖动
JAVA_OPTS="-server -Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
# GC:JDK 17 推荐 G1
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
# 故障排查:OOM 时自动 dump,JVM 崩溃时生成 hs_err 日志
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/tomcat/logs/"
JAVA_OPTS="$JAVA_OPTS -XX:+ExitOnOutOfMemoryError"
# 时间与随机数(Tomcat 启动慢的经典原因)
JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
JAVA_OPTS="$JAVA_OPTS -Duser.timezone=Asia/Shanghai -Dfile.encoding=UTF-8"
# 可选:开启 JMX 远程监控(注意加认证,不要裸奔)
# JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=9010 -Dcom.sun.management.jmxremote.authenticate=false"
EOF
sudo chmod +x /opt/tomcat/bin/setenv.sh内存设置原则:
-Xms与-Xmx设为相同值,避免堆动态扩容带来的停顿。- 堆大小不超过物理内存的 60%~70%,要给元空间、线程栈、DirectMemory 和系统留余量。4GB 内存的机器建议
-Xmx2g。 - 元空间(Metaspace)存类元数据,应用类多时容易
OutOfMemoryError: Metaspace,建议设 256~512m。 - 每线程栈默认约 1MB,
maxThreads=400即 400MB 额外开销,也要算进内存预算。
五、WAR 部署与 systemd 托管
5.1 部署方式对比
| 方式 | 操作 | 优点 | 适用 |
|---|---|---|---|
放 WAR 到 webapps/ | 拷贝文件,Tomcat 自动解压部署 | 最简单 | 小项目、允许短暂停机 |
| Manager Web 界面 | 浏览器上传 WAR | 无需 SSH | 需开启 tomcat-users.xml 配置 |
脚本 + curl 调 Manager API | CI/CD 自动发布 | 可自动化 | 持续集成 |
解压目录 + Context 指定 | 直接替换目录内容 | 可控性强 | 生产推荐,便于灰度 |
生产推荐做法(避免直接覆盖导致的类锁问题):
# 1. 停止应用或整机(小站点可直接停 Tomcat)
sudo systemctl stop tomcat
# 2. 备份旧版本(回滚的保障)
sudo -u tomcat cp -a /opt/tomcat/webapps/myapp /opt/tomcat/webapps/myapp.bak.$(date +%F)
# 3. 部署新包
sudo -u tomcat rm -rf /opt/tomcat/webapps/myapp
sudo -u tomcat unzip -q /tmp/myapp.war -d /opt/tomcat/webapps/myapp
# 4. 清理 work 缓存的 JSP 编译产物(改了 JSP 必须清)
sudo -u tomcat rm -rf /opt/tomcat/work/Catalina/localhost/myapp
# 5. 启动并观察
sudo systemctl start tomcat
tail -f /opt/tomcat/logs/catalina.out5.2 systemd 服务文件
# /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat 11
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
Environment="CATALINA_PID=/opt/tomcat/tomcat.pid"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
Restart=on-failure
RestartSec=10
# 提高文件描述符限制,支持更多并发连接
LimitNOFILE=65536
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now tomcat
systemctl status tomcat六、Nginx 反向代理对接
Tomcat 前面挂 Nginx 是生产标配:Nginx 处理 80/443、SSL、静态资源和限流,动态请求转发给 Tomcat。
upstream tomcat_backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 64;
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
client_max_body_size 50m;
# 静态资源交给 Nginx,不打扰 Tomcat
location ~* \.(jpg|jpeg|png|gif|css|js|woff2|ico)$ {
root /opt/tomcat/webapps/myapp;
expires 30d;
access_log off;
}
location / {
proxy_pass http://tomcat_backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 配合 keepalive
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 让 Java 端识别 https
proxy_connect_timeout 5s;
proxy_read_timeout 120s;
}
}关键点:X-Forwarded-Proto 必须传,否则 Spring Security 等框架会误判为 http 而反复重定向。同时 Tomcat 侧要加 RemoteIpValve 让 request.getRemoteAddr() 拿到真实 IP。另外,proxy_pass 后面的 upstream 建议开启 keepalive 64,让 Nginx 与 Tomcat 之间复用长连接,减少三次握手与 TIME_WAIT 堆积;对应地 Tomcat 连接器的 keepAliveTimeout 要略大于 Nginx 的 keepalive_timeout,否则会出现大量半关闭连接。
企业QQ咨询




