Tomcat服务器部署项目教程

结论: 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 版本最低 JDKServlet/JSP 规范说明
Tomcat 9JDK 8Servlet 4.0 / JSP 2.3老项目维护,包名仍是 javax.*
Tomcat 10.1.xJDK 11Servlet 6.0 / JSP 3.1**包名从 javax. 改为 jakarta.**,迁移需注意
Tomcat 11(截至 2026 年为最新主线)JDK 17Servlet 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 为例):

bash
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 安装与目录结构

官方推荐用二进制包解压安装,不要用系统包管理器装(版本往往滞后且目录被拆散)。

bash
# 创建专用运行用户(绝不用 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/临时文件

启动与验证:

bash
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 的层次结构。下面给出生产常用的关键配置项说明。

xml


  

  
    
    

    
      
        
        

        
      
    
  

关键参数含义:

参数建议值说明
port8080(或改其他)HTTP 监听端口
protocolHTTP/1.1(默认 NIO)高并发可改 org.apache.coyote.http11.Http11Nio2Protocol
maxThreads200~800最大工作线程数,即最大并发请求数
acceptCount100~500队列长度,满了就拒绝连接
maxConnections5000~20000NIO 模式下可保持的最大连接数
connectionTimeout20000ms连接超时
enableLookupsfalse必须关,否则反向 DNS 查询严重拖慢
URIEncodingUTF-8解决 GET 参数中文乱码
autoDeployfalse生产关闭热部署,避免运行时被替换
reloadablefalse生产关闭类重载(开销大且可能内存泄漏)
unpackWARstrue自动解压 WAR

四、JVM 参数与 setenv.sh

Tomcat 的默认堆内存很小(通常 1/4 物理内存或更低),生产必须显式设置。配置文件是 bin/setenv.sh(默认不存在,需自建,Tomcat 启动时会自动读取)。

bash
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 APICI/CD 自动发布可自动化持续集成
解压目录 + Context 指定直接替换目录内容可控性强生产推荐,便于灰度

生产推荐做法(避免直接覆盖导致的类锁问题):

bash
# 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.out

5.2 systemd 服务文件

ini
# /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.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now tomcat
systemctl status tomcat

六、Nginx 反向代理对接

Tomcat 前面挂 Nginx 是生产标配:Nginx 处理 80/443、SSL、静态资源和限流,动态请求转发给 Tomcat。

nginx
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,否则会出现大量半关闭连接。

xml