Tomcat 目录结构
Tomcat 的核心目录结构清晰定义了其功能和配置的存放位置,了解它们是理解其工作原理的第一步。
| 目录/文件 |
核心用途 |
与配置的关系 |
bin/ |
存放启动(startup.sh/bat)、关闭(shutdown.sh/bat)及环境设置脚本。 |
脚本中会引用 conf 下的配置来启动 JVM。 |
conf/ |
核心配置目录。存放全局配置文件。 |
server.xml (主配置), web.xml (全局默认配置), tomcat-users.xml (用户权限), context.xml (上下文默认配置)。 |
logs/ |
存放运行日志(catalina.out, localhost.log 等)。 |
记录 server.xml 中定义的 Host 和 Context 的运行状态及错误。 |
webapps/ |
默认应用部署目录。将 WAR 包或文件夹放入此处会自动部署。 |
每个子目录代表一个 Web 应用,内部包含该应用特有的 WEB-INF/web.xml。 |
各目录之间的关键关系如下:
conf/server.xml 定义了 Tomcat 的整体架构(端口、主机、引擎)。
conf/web.xml 定义了所有 Web 应用的默认行为(如默认 Servlet 映射、MIME 类型、会话超时时间)。
webapps/<app>/WEB-INF/web.xml 是特定应用的配置,它会覆盖或补充 conf/web.xml 中的配置。
webapps/ 下的文件内容受 server.xml 中<Host>标签的 appBase 属性控制。
Tomcat 核心配置文件
server.xml
server.xml 是 Tomcat 的主配置文件,位于 conf/ 目录下,它定义了整个容器的层级结构和核心组件,是理解其架构的关键。
<Server>: 根元素,代表整个 Tomcat 实例。
<Service>: 包含一个 Engine 和多个 Connector。用于将来自不同端口的请求关联到同一个处理引擎。
<Connector>: 连接器。负责接收客户端请求(HTTP/HTTPS/AJP)。
- 关键属性:
port (端口), protocol (协议处理器), connectionTimeout。
- 作用:建立 TCP 连接,解析 HTTP 报文,生成 Request/Response 对象传给 Engine。
<Engine>: 引擎。处理来自 Connector 的请求。一个 Service 只能有一个 Engine。
- 关键属性:
defaultHost (默认主机名)。
- 作用:根据请求的域名将请求分发给对应的 Host。
<Host>: 虚拟主机。对应一个域名(如 localhost 或 www.example.com)。
- 关键属性:
name, appBase (应用基础目录,默认为 webapps)。
- 作用:管理该域名下的所有 Web 应用(Context),实现多站点部署。
<Context>: Web 应用上下文。代表一个具体的 Web 应用(如 /myapp)。
- 作用:定义应用的路径、资源链接、会话配置等。
- 注意:在 Tomcat 9+ 中,通常建议在
conf/Catalina/localhost/ 下单独配置 XML 文件,而不是直接写在 server.xml 中,以避免因修改应用配置而重启整个服务。
一个典型的 server.xml 配置示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Server: 根元素,代表整个 Tomcat 实例。
port="8005": 监听关闭命令的端口
shutdown="SHUTDOWN": 关闭指令字符串。 (telnet localhost 8005 发送 SHUTDOWN 可停止服务)。
-->
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<!-- Service: 包含一个 Engine 和多个 Connector。名称通常默认为 "Catalina"。-->
<Service name="Catalina">
<!--
Connector: 连接器,负责接收客户端请求。
protocol: 使用 NIO (非阻塞 IO) 协议。
port: 服务端口,默认 8080。
connectionTimeout: 连接超时时间 (毫秒)。
redirectPort: 如果收到 HTTPS 请求,重定向到此端口 (默认 8443)。
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="200"
minSpareThreads="25" />
<!--
Engine: 处理请求的核心容器。
defaultHost: 当请求的 Host 头不匹配任何定义的 Host 时,使用此主机。
-->
<Engine name="Catalina" defaultHost="localhost">
<!--
Host: 虚拟主机,对应一个域名或 IP。
name: 主机名 (如 www.example.com 或 localhost)。
appBase: 应用部署的基础目录,默认为 webapps。
unpackWARs: 是否自动解压 WAR 包。
autoDeploy: 是否自动检测并部署新的应用。
-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- AccessLogValve: 记录访问日志 -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<!--
Context: 代表一个具体的 Web 应用。
注意:在生产环境中,通常不建议直接在 server.xml 中配置 Context,
而是建议在 $CATALINA_BASE/conf/Catalina/localhost/ 目录下创建单独的 XML 文件。
path: 应用的访问路径 (如 /myapp)。
docBase: 应用的实际物理路径 (可以是绝对路径,也可以是 appBase 下的相对路径)。
-->
<Context path="/myapp" docBase="myapp" reloadable="true" />
<!-- 默认 Context: 代表根路径 "/" 的应用 (通常是 manager 或 host-manager,或者默认的欢迎页) -->
<!-- <Context path="" docBase="ROOT" /> -->
</Host>
</Engine>
</Service>
</Server>
web.xml
Tomcat 中有两个层级的 web.xml,它们共同定义了 Web 应用的行为:
- 1、全局默认配置 (
conf/web.xml):为服务器上所有应用提供默认设置。它定义了默认的 Servlet(如 default 处理静态资源,jsp 处理 JSP 文件)、默认的 MIME 映射、会话超时时间(默认 30 分钟)、欢迎页面列表等。
- 2、应用级配置 (
WEB-INF/web.xml):位于每个 Web 应用内部,用于配置该应用特有的 Servlet、Filter、Listener、初始化参数、安全约束等。
加载顺序:Tomcat 启动时先加载 conf/web.xml,再加载应用的 WEB-INF/web.xml。应用配置会覆盖全局配置中同名的设置。
一个典型的应用级 web.xml 配置示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 1. 应用描述 -->
<display-name>My Demo Application</display-name>
<description>This is a sample web application for Tomcat analysis.</description>
<!-- 2. 上下文参数 (Context Param): 全局初始化参数,所有 Servlet 都可访问 -->
<context-param>
<param-name>dbDriver</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>dbUrl</param-name>
<param-value>jdbc:mysql://localhost:3306/mydb</param-value>
</context-param>
<!-- 3. Listener: 监听器,用于监听应用生命周期事件 -->
<listener>
<listener-class>com.example.listener.AppInitListener</listener-class>
</listener>
<!-- 4. Filter: 过滤器,拦截请求进行处理 (如编码设置、权限验证) -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequest</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 拦截所有请求 -->
</filter-mapping>
<!-- 5. Servlet: 核心组件,处理业务逻辑 -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.servlet.HelloServlet</servlet-class>
<!-- 初始化参数,仅该 Servlet 可见 -->
<init-param>
<param-name>greeting</param-name>
<param-value>Hello World</param-value>
</init-param>
<!-- 加载顺序: 数字越小越先加载,负数或无表示按需加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern><!-- 访问 http://host:port/app/hello -->
</servlet-mapping>
<!-- 另一个 Servlet 示例 -->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.example.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user/*</url-pattern><!-- 路径映射,匹配 /user/xxx -->
</servlet-mapping>
<!-- 6. Welcome File List: 默认首页列表 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
</welcome-file-list>
<!-- 7. Error Page: 自定义错误页面 -->
<error-page>
<error-code>404</error-code>
<location>/error/404.html</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error/general_error.jsp</location>
</error-page>
</web-app>
Catalina 容器组件架构
Catalina 是 Apache Tomcat 的核心 Servlet 容器引擎,其核心架构设计遵循了分层处理和责任链模式。理解其内部组件如何协同工作是掌握 Tomcat 工作机理的关键。
主要组件如下:
- Connector (连接器):基于 NIO/NIO2/APR 模型。负责网络通信,解析 HTTP 协议,将原始请求封装成标准的 Request 对象,并将 Response 对象写回客户端。
- Container (容器):负责处理业务逻辑,内部也是分层结构:
- Engine:顶级容器,匹配 Host。
- Host:虚拟主机容器,匹配 Context。
- Context:应用容器,匹配 Wrapper (Servlet)。
- Wrapper:最底层容器,管理单个 Servlet 的生命周期。
Catalina 容器处理请求的核心机制是 管道-阀门(Pipeline-Valve)责任链模式:
- Pipeline(管道):
- 定义:每个容器组件(Engine、Host、Context、Wrapper)都拥有一个唯一的 Pipeline 对象。
- 作用:它是请求处理的“传送带”。当请求进入某个容器时,就会进入该容器的 Pipeline。
- 结构:Pipeline 内部维护了一个阀门(Valve)链表和一个特殊的基础阀门(BasicValve)。
- Valve(阀门):
- 定义:具体的处理单元,实现了
org.apache.catalina.Valve 接口。
- 作用:执行具体的业务逻辑或非业务逻辑(如打印日志、检查权限)。
- 结构分类:
- 普通阀门:可以添加多个,按顺序执行。例如:AccessLogValve(访问日志)、RemoteAddrValve(IP 过滤)、SingleSignOnValve(单点登录)。
- 基础阀门:每个 Pipeline 只能有一个,且必须位于链表的末尾。它的职责通常是调用下一个层级的容器(例如:Host 的 Basic Valve 负责找到并调用对应的 Context)。4个容器的 BaseValve 分别是
StandardEngineValve 、StandardHostValve 、StandardContextValve 和StandardWrapperValve。

一次完整的 HTTP 请求处理流程
现在,让我们把上述所有组件串联起来,看看当一个用户发起 HTTP 请求(例如 http://localhost:8080/myapp/hello)时,Tomcat 是如何一步步处理的:
- 建立连接:客户端与 Tomcat 的 Connector (监听 8080 端口) 建立 TCP 连接。
- 解析请求:Connector 读取 HTTP 报文,解析出 URL、Header、Body 等信息,封装成 HttpServletRequest 和 HttpServletResponse 对象。
- 映射 Engine:Connector 将请求交给关联的 Service 中的 Engine。
- 映射 Host:Engine 根据请求头中的 Host 字段(如 localhost),在其管理的 Host 列表中找到匹配的 Host 组件。如果未找到,使用 defaultHost。
- 映射 Context:Host 根据 URL 的路径部分(
/myapp),在其管理的 Context 列表中找到对应的 Context(即 Web 应用)。此时,Host 的 Pipeline 中的 Valve 会执行(如访问日志)。
- 映射 Wrapper (Servlet):Context 根据剩余的路径(
/hello),查阅该应用的 web.xml 或注解配置,找到匹配的 Servlet (Wrapper)。如果找不到,可能由默认的 DefaultServlet 处理(返回 404 或静态资源)。在此过程中,Context 配置的 Filter 链(filterChain)会被执行。
- 执行 Servlet:Wrapper 调用目标 Servlet 的
service() 方法( doGet/doPost 等)。业务逻辑执行完毕,数据写入 Response 对象。
- 响应返回:响应对象沿原路返回(Wrapper -> Context -> Host -> Engine -> Connector)。Connector 将 Response 对象序列化为 HTTP 响应报文,发送给客户端。
- 关闭连接:根据 HTTP 协议版本(Keep-Alive 设置),决定是关闭连接还是保持连接等待下一次请求。
这个流程清晰地展示了 Tomcat 如何通过分层容器和责任链模式,将一个复杂的 HTTP 请求处理分解为多个清晰、可扩展的步骤。掌握这个流程,对于排查问题、性能调优以及深入理解 Java Web 技术栈都至关重要。
|