CATALINA_HOME是只读程序根目录,含bin/lib/conf等共享资源;CATALINA_BASE是可写实例根目录,含logs/temp/work/webapps等独占路径;多实例必须独立CATALINA_BASE、共用CATALINA_HOME,否则日志覆盖、临时文件冲突、webapps误删。

Tomcat 单机跑没问题,但一上生产就卡在环境变量没配对、多实例端口冲突、Nginx 转发 404 或 session 丢失——这些问题基本都出在 CATALINA_HOME 和 CATALINA_BASE 混用、server.xml 端口未隔离、以及 Nginx 的 upstream 配置漏了 proxy_set_header 这几处。
为什么 CATALINA_HOME 和 CATALINA_BASE 必须分开设
很多人把两个变量全指向同一个目录,结果启动第二个 Tomcat 实例时日志写不进 logs/、临时文件覆盖、甚至 webapps/ 被误删。根本原因是:
-
CATALINA_HOME是只读的“程序根目录”,含bin/、lib/、conf/server.xml等共享资源 -
CATALINA_BASE是可写的“实例根目录”,含logs/、temp/、work/、webapps/等运行时独占路径 - 多实例必须让每个实例的
CATALINA_BASE独立,CATALINA_HOME可共用(比如都指向/usr/local/tomcat)
Linux 下推荐用独立 profile 文件(如 /etc/profile.d/tomcat1.sh),内容示例:
export CATALINA_HOME=/usr/local/tomcat export CATALINA_BASE=/usr/local/tomcat/tomcat1 export PATH=$CATALINA_HOME/bin:$PATH
别往 /etc/profile 里硬塞多个 export,容易被覆盖或顺序错乱。
server.xml 端口冲突的三个关键位置
一个 Tomcat 实例要跑通,至少得避开三类端口重叠:管理端口、HTTP 接入端口、AJP 连接端口。默认是 8005、8080、8009,第二实例常见错误是只改了 8080 → 8081,却忘了其他两个:
-
<Server port="8005">:shutdown 命令监听端口,多实例必须各不相同(如8006) -
<Connector port="8080" protocol="HTTP/1.1">:对外 HTTP 端口,建议用8080、8081、8082递增 -
<Connector port="8009" protocol="AJP/1.3">:供 Nginx/Apache 反向代理用,也必须唯一(如8010)
检查方式很简单:netstat -tuln | grep ':80' 看端口是否已被占用;启动失败时日志里会明确报 Address already in use。
Nginx upstream 配置 session 丢失的根源
明明两个 Tomcat 都能单独访问,但走 Nginx 后登录态总掉——大概率是没透传原始 Host 和 Cookie 头,或者没启用 sticky session。
- 基础反向代理必须加这两行,否则 Tomcat 收不到真实请求头:
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
- 如果应用没做 session 共享(如没配 Redis 或 JDBC Session Store),就得靠 Nginx 的 sticky 保证同一用户始终打到同一台 Tomcat:
upstream backend {
ip_hash; # 简单按 IP 哈希(适合内网)
# 或用第三方模块:sticky cookie srv_id expires=1h domain=.example.com path=/;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}注意:ip_hash 在客户端走代理或 NAT 时可能失效,线上更稳妥的是用 sticky 模块 + cookie,但需提前编译进 Nginx。
负载均衡后 404 或静态资源加载失败
这不是 Tomcat 的问题,而是 Nginx 没区分动静资源。典型表现:JSP 页面能打开,但 CSS/JS 报 404;或首页 200,子页面全 404。
- 原因:Nginx 默认把所有请求都 proxy_pass 给 Tomcat,而 Tomcat 处理静态文件效率低,且常因 context path 不一致导致路径解析错位
- 解法:用 location 区分,让 Nginx 直接 serve 静态资源,只把动态请求转发过去:
location / {
try_files $uri @tomcat;
}
location @tomcat {
proxy_pass http://backend;
proxy_set_header Host $host;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}关键是 try_files $uri @tomcat ——先查文件是否存在,不存在才交给 Tomcat,避免无谓转发和路径拼接错误。
真正麻烦的从来不是配置项本身,而是多个实例间 CATALINA_BASE 目录权限不对、conf/logging.properties 里指定了绝对路径导致日志写失败、或者 Nginx 编译时没带 --with-http_stub_status_module 导致无法监控 upstream 状态——这些细节不验证,上线就出事。

















