Tomcat 虚拟主机与多应用部署
概述
一台 Tomcat 可以同时服务多个域名、承载多个应用——虚拟主机(Virtual Host)与 Context 是实现这一切的两个关键概念。本文拆解 Host 的路由原理、Context 的部署方式,以及热部署机制,最后给出多应用、多域名环境下的规划建议。
一、虚拟主机:一台服务器,多个域名
1.1 虚拟主机原理
虚拟主机让一个 Tomcat 实例对外表现为多个站点。请求到达后,Tomcat 根据 HTTP 请求的 Host 头选择对应的 Host 容器:
请求:GET /login HTTP/1.1
Host: www.shop.com ← 关键:Host 头
│
▼
Engine.defaultHost 兜底 ◀──未匹配──┤
▼
www.shop.com(Host 容器)
│
▼
webapps2/ 下的应用匹配规则:优先精确匹配 Host 头与 Host.name;无匹配时落入 Engine.defaultHost。
1.2 多域名配置
<Engine name="Catalina" defaultHost="localhost">
<!-- 站点一:官网 -->
<Host name="www.example.com" appBase="webapps-site"
unpackWARs="true" autoDeploy="true">
<Alias>example.com</Alias>
</Host>
<!-- 站点二:管理后台 -->
<Host name="admin.example.com" appBase="webapps-admin"
unpackWARs="true" autoDeploy="true" />
</Engine>| 元素 | 说明 |
|---|---|
Host.name | 主域名,匹配 Host 头 |
Alias | 别名域名(如裸域名 example.com),多个别名可叠加 |
appBase | 该 Host 的应用部署目录,各 Host 独立 |
目录规划:每个 Host 独立 appBase,应用之间互不干扰,这是多租户场景的基本隔离手段。
二、Context:Web 应用部署
2.1 Context 的定位
Context 代表一个 Web 应用,Tomcat 用它与 URL 前缀(path)建立映射。Context 可以通过三种位置配置,优先级从高到低:
| 位置 | 示例 | 生效时机 |
|---|---|---|
应用内 META-INF/context.xml | webapps/myapp/META-INF/context.xml | 部署时 |
conf/Catalina/<host>/<app>.xml | conf/Catalina/localhost/myapp.xml | 热部署(推荐) |
server.xml 内 <Context> | 不推荐 | 需重启 |
2.2 path 与 docBase 的映射
| 属性 | 示例 | 含义 |
|---|---|---|
path | /order | 访问路径前缀 |
docBase | /data/apps/order | 应用文件的实际位置 |
http://localhost:8080/order/list
└─┬─┘ └─┘
path 应用内路径- 自动部署:WAR 放入
appBase后,Tomcat 以文件名自动生成 Context(myapp.war→ path/myapp),无需手写配置 - 外部 Context:应用目录在 Tomcat 之外时,通过
docBase指向:
<!-- conf/Catalina/localhost/order.xml -->
<Context docBase="/data/apps/order" reloadable="false" />此时访问路径固定为 /order(文件名即 path)。
2.3 Context 常用属性
| 属性 | 默认值 | 说明 |
|---|---|---|
reloadable | false | 类变化自动重载(开发环境可开) |
unpackWARs | true | WAR 是否解压运行 |
sessionCookiePath | — | 会话 Cookie 路径,多应用共用域名时隔离 |
privileged | false | 是否允许访问容器级 Servlet |
xmlNamespaceAware | false | web.xml 命名空间解析 |
useHttpOnly | true | 会话 Cookie 加 HttpOnly |
cookieProcessor | — | Cookie 解析器(Rfc6265CookieProcessor 等) |
三、多应用部署的四种形态
| 形态 | 配置方式 | 访问路径 | 适用场景 |
|---|---|---|---|
| appBase 自动部署 | WAR 放入 webapps | /应用名 | 最简单 |
| 根路径部署 | 应用名 ROOT | / | 站点主应用 |
| 外部目录映射 | <Context docBase="..."> | /任意路径 | 应用与 Tomcat 解耦 |
| 多 Host 多应用 | 每个 Host 独立 appBase | 按域名区分 | 多站点/多租户 |
3.1 根路径部署(ROOT)
把应用部署为根路径,URL 不含应用名前缀:
# 方式一:WAR 命名为 ROOT.war
cp order.war webapps/ROOT.war
# 方式二:外部映射
# conf/Catalina/localhost/ROOT.xml
<Context docBase="/data/apps/order" />访问 http://localhost:8080/ 即直达应用。适用于"一个站点对应一个主应用"的场景,注意 ROOT 应用与 Host.appBase 下的其他应用路径不能冲突。
3.2 外部目录部署的价值
/data/apps/
├── order/ ← 应用代码(版本管理、CI/CD 发布)
└── user/
/opt/tomcat/conf/Catalina/localhost/
├── order.xml ← 指向 /data/apps/order
└── user.xml- 应用目录不在 webapps 内,发布新版本时只替换
/data/apps下的文件 - 通过替换 XML 文件即可快速调整映射,无需动 server.xml
四、热部署机制
4.1 触发条件
autoDeploy="true"(Host 默认)时,后台线程周期扫描 appBase:
| 变化 | 动作 |
|---|---|
| 新增 WAR/目录 | 部署新 Context |
| 删除 WAR/目录 | 卸载对应 Context |
| WAR 修改时间变化 | 重新部署 |
conf/Catalina/<host>/*.xml 变化 | 重新部署对应 Context |
扫描周期由 Host 的 backgroundProcessorDelay 控制(默认 10 秒)。
4.2 部署过程
发现新 WAR
→ 创建 StandardContext
→ 解压/读取 web.xml
→ 创建 WebappClassLoader
→ 初始化 Servlet 上下文监听器
→ 启动容器(Context.start())
→ 注册到 Engine 的路由表4.3 热部署的安全与性能影响
| 关注点 | 说明 |
|---|---|
| 部署风暴 | 大 WAR 同时变更时触发频繁重部署,需错峰发布 |
| 内存泄漏 | 热重载可能残留旧 ClassLoader(详见类加载机制篇) |
| 竞争条件 | 部署中访问应用可能 404/报错,配合负载均衡摘流量 |
| 生产策略 | autoDeploy="false",采用运维工具或 Manager 显式部署 |
生产建议:autoDeploy="false" + deployOnStartup="false" 的 Host 配合 CI/CD 明确发布窗口,避免运行时意外重部署。
五、部署相关的管理与安全
5.1 Manager 管理应用
Tomcat 自带的 /manager 与 /host-manager 提供部署/卸载/会话管理界面与 API:
<!-- conf/tomcat-users.xml 中授权 -->
<role rolename="manager-gui" />
<role rolename="manager-script" />
<user username="admin" password="xxx" roles="manager-gui,manager-script" />| 角色 | 权限 |
|---|---|
manager-gui | HTML 管理界面 |
manager-script | 脚本/API 部署(CI/CD 使用) |
manager-jmx | JMX 访问 |
host-manager | 虚拟主机管理 |
5.2 部署安全清单
- 禁用 Manager:不需要管理功能时删除
webapps/manager与webapps/host-manager - 强密码 + 网络限制:Manager 仅绑定内网或加 IP 白名单
- 文件权限:webapps 与 appBase 目录写入权限收紧到专用账号
- WAR 校验:来自不可信来源的 WAR 可能含恶意 Servlet,仅部署可信构建产物
- 禁止上传目录执行:上传目录置于应用可写但不可执行的位置
六、多应用规划实践
6.1 多域名多应用的拓扑示例
┌──────────────────────────────┐
www.example.com ─▶│ Host: www.example.com │
│ appBase: webapps-site │
│ └─ ROOT(官网主站) │
admin.example.com▶│ Host: admin.example.com │
│ appBase: webapps-admin │
│ └─ admin(管理后台) │
api.example.com ─▶│ Host: api.example.com │
│ appBase: webapps-api │
│ └─ api(REST 服务) │
└──────────────────────────────┘6.2 规划建议
| 维度 | 建议 |
|---|---|
| 应用间是否隔离 | 隔离要求高 → 多 Host 或多实例(CATALINA_BASE) |
| 共用库 | 相同依赖统一放 $CATALINA_HOME/lib,避免每个应用重复打包 |
| 会话 Cookie | 多应用共域名时设置 sessionCookiePath 区分 |
| 端口规划 | 一个实例多应用共享端口;多实例则端口错开 |
| 发布策略 | 外部目录 + CI/CD 拷贝 + 显式 reload,避免热部署竞态 |
6.3 单实例多应用 vs 多实例
| 维度 | 单实例多应用 | 多实例(多 CATALINA_BASE) |
|---|---|---|
| 资源隔离 | 共享 JVM,一个 OOM 全挂 | 独立 JVM,故障隔离 |
| 运维复杂度 | 低 | 高(多进程管理) |
| 适用场景 | 应用少、信任边界内 | 强隔离、独立调优、灰度发布 |
七、常见问题排查
| 现象 | 原因 | 处理 |
|---|---|---|
| 访问域名 404 | Host 未匹配或应用未部署到对应 appBase | 检查 Host.name / Alias / defaultHost |
| 部署新 WAR 无反应 | autoDeploy=false 或扫描未触发 | 检查 Host 配置或手动 Manager 部署 |
| 路径 404 | Context path 与期望不符 | 确认 WAR 名 / docBase 映射 |
| 修改 server.xml 不生效 | server.xml 不热加载 | 重启实例 |
| 部署后偶发 503 | Context 启动未完成 | 依赖探活(startup 完成后再放流量) |
参考链接: