打开 Tomcat 的虚拟 server.xml,你会看到一个 `
我刚开始接触 Tomcat 那会儿,虚拟觉得虚拟主机这概念挺唬人。主机后来拆开看,虚拟其实逻辑很朴素。主机你想象一栋写字楼,虚拟地址只有一个,主机但里面注册了几十家公司。虚拟快递员送到楼下,前台看一眼收件公司名,把包裹分到对应楼层。Tomcat 干的事一模一样——`
装完 Tomcat,server.xml 里默认就有一个 Host:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
这个 Host 管着 webapps 目录下所有的应用。你把 war 包往里一扔,Tomcat 自动解压部署,访问 `http://localhost:8080/你的应用名` 就能打开。大部分人平时就用这一个,没觉得需要配多个。
但假设你手上有两个项目,一个是公司官网,一个是内部管理系统,域名分别是 `www.example.com` 和 `admin.example.com`。你不想为这俩各买一台服务器,也不想在一个 Tomcat 里跑两个实例占两份内存。虚拟主机就是干这个的。
在 `
<Host name="admin.example.com" appBase="/data/admin-web" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/data/admin-web/ROOT" /> </Host>
关键在 `name` 属性。Tomcat 收到请求后,拿 HTTP 请求头里的 `Host` 字段跟各个 `
DNS 那边把两个域名都解析到同一台服务器的 IP。用户访问 `www.example.com`,请求到了 Tomcat,Tomcat 一看 Host 头是 `www.example.com`,找到对应的 `
appBase 路径别写相对路径。我见过有人写 `appBase="webapps2"`,以为会在 Tomcat 根目录下找,结果 Tomcat 按 `CATALINA_HOME` 相对路径解析,有时候跟你想的不一样。写绝对路径最省心。
Context 的 path 属性在嵌套 Host 里要小心。如果你在 Host 里手动配了 `
还有个细节:Tomcat 8 以后,`autoDeploy` 和 `unpackWARs` 的默认行为有调整。如果你从旧版本迁过来,部署方式可能跟预期不一样。建议显式写上这两个属性,别依赖默认值。
有人会把 Tomcat 的虚拟主机跟 Apache HTTP Server 的 VirtualHost 搞混。Apache 那个是在 Web 服务器层面按域名分流,可以反代到不同的后端。Tomcat 这个是 Servlet 容器内部的机制,请求已经进了 Tomcat 才做分流。实际生产环境里,常见做法是 Nginx 或 Apache 在前面按域名分流,后面接多个 Tomcat 实例,或者接一个 Tomcat 的多个 Host。两种方式不冲突,看你的架构需求。
我自己的习惯是:如果几个小站点流量不大,共用一台服务器,直接在 Tomcat 里配多个 Host,省事。如果站点之间需要独立重启、独立调 JVM 参数,那就拆成多个 Tomcat 实例,前面用 Nginx 分流。虚拟主机解决的是“一个实例跑多个站”的问题,不是万能的。
配完之后记得重启 Tomcat。改 server.xml 不像改 web.xml 那样能热加载,必须重启才生效。重启前用 `catalina.sh configtest` 检查一下 XML 语法,省得启动失败还得翻日志找原因。