Tomcat 整体架构与核心组件
概述
Tomcat 是 Apache 软件基金会维护的开源 Servlet 容器,也是 Java Web 应用事实上的标准运行时。它不只是把 Servlet 跑起来那么简单——一次 HTTP 请求从网络进入,到业务代码执行,再到响应返回,中间经过的组件层次、线程模型与生命周期管理,共同构成了 Tomcat 的设计骨架。
理解 Tomcat 的架构,先记住三个词:Catalina(容器)、Coyote(连接器)、Jasper(JSP 引擎)。三者分工明确,又通过标准接口协同工作。
一、三大引擎的职责划分
| 引擎 | 全称/定位 | 核心职责 | 关键类 |
|---|---|---|---|
| Catalina | Servlet 容器实现 | 管理生命周期、维护容器层级、执行 Servlet 规范 | Catalina、StandardServer、StandardEngine |
| Coyote | 通信层 | 监听端口、解析协议、管理连接与线程、生成 Request/Response | Http11NioProtocol、AbstractEndpoint |
| Jasper | JSP 引擎 | 将 JSP 编译为 Servlet 字节码 | JspC、JasperInitializer |
三个引擎的协作关系如下:
Client (HTTP/HTTPS/AJP)
│
▼
┌─────────┐ ┌──────────────────────┐
│ Coyote │ ─────▶ │ Request / Response │ 协议无关的请求对象
│(Connector)│ └──────────────────────┘
└─────────┘ │
▼
┌───────────────┐
│ Catalina │ 容器层:Engine → Host → Context → Wrapper
│(Container) │
└───────────────┘
│
▼
┌───────────────┐
│ Servlet │ 业务代码(或 JSP → Jasper 编译的 Servlet)
└───────────────┘Coyote 只关心"网络通信",不懂 Servlet 规范;Catalina 只关心"容器与请求分发",不碰网络细节。二者通过 Request / Response 门面对象解耦——这正是 Tomcat 能同时支持 HTTP/1.1、HTTP/2、AJP 等不同协议的原因。
二、组件树:Server → Service → Connector/Container
Tomcat 的配置文件 server.xml 描述的是一棵组件树,顶层是 Server,逐级向下:
Server ← 整个 Tomcat 实例(一个 JVM 进程)
└── Service ← 一组 Connector + 一个 Engine 的绑定
├── Connector (HTTP/1.1 8080) ← Coyote,监听端口,接收请求
├── Connector (AJP/1.3 8009) ← 可选,与 Apache/Nginx 通信
└── Engine ← Catalina 入口,负责请求分发
├── Host (localhost) ← 虚拟主机,一个域名一个 Host
│ ├── Context (/app1) ← 一个 Web 应用
│ │ └── Wrapper ← 一个 Servlet 实例的包装
│ └── Context (/app2)
└── Host (example.com)每个组件的含义:
| 组件 | 含义 | 典型配置 | 对应 Java 类 |
|---|---|---|---|
| Server | Tomcat 实例,管理全局资源与生命周期 | 唯一 | StandardServer |
| Service | Connector 与 Engine 的绑定集合 | 可多个 | StandardService |
| Connector | 协议监听端点(端口 + 协议) | HTTP 8080、AJP 8009 | Connector |
| Engine | 顶层容器,接收所有 Host 的请求 | 唯一 | StandardEngine |
| Host | 虚拟主机,通过域名区分 | localhost | StandardHost |
| Context | Web 应用,对应一个应用目录 | /myapp | StandardContext |
| Wrapper | 单个 Servlet 的包装 | 每个 Servlet 一个 | StandardWrapper |
Engine、Host、Context、Wrapper 都是 Container 接口的实现,构成一条父子链。请求沿着这条链自上而下传递,每一层都先执行自己的 Pipeline,再把请求交给下一层。
三、Container 与 Pipeline-Valve 架构
3.1 Container 四层模型
Servlet 规范只定义了 Servlet、ServletRequest、ServletResponse 三个核心对象,Tomcat 在其上扩展出四层容器:
Engine (localhost 应用的引擎级过滤器)
→ Host (虚拟主机级过滤器)
→ Context (应用级过滤器,如 web.xml 的 Filter)
→ Wrapper (Servlet 包装)每层 Container 都可以参与请求处理,这就是为什么 Tomcat 里"过滤器链"可以在不同层级生效:Filter 注册在 Context 层,而容器自己的管道阀(Valve)则分布在各层。
3.2 Pipeline 与 Valve
每个 Container 内部都有一条 Pipeline(管道),管道上串着多个 Valve(阀)。请求进入容器后,按 Valve 顺序执行,最后一个 Valve 负责把请求传递给子容器。
// 管道接口的核心方法
public interface Pipeline {
Valve getBasic(); // 基础阀:链尾的默认处理器
void setBasic(Valve valve);
void addValve(Valve valve); // 添加自定义阀
}以 StandardEngineValve 为例,它是 Engine 管道的 Basic Valve,逻辑很简单:
public class StandardEngineValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
// 1. 从请求中取出 Host(根据域名匹配)
Host host = request.getHost();
if (host == null) {
response.sendError(400); // 没有匹配的虚拟主机
return;
}
// 2. 交给 Host 容器的管道继续处理
host.getPipeline().getFirst().invoke(request, response);
}
}getFirst() 返回管道上第一个 Valve,Valve 链会逐级向下传递,直到 Wrapper 层调用 FilterChain 与目标 Servlet。整个机制是职责链模式在容器层级的应用,每一层都只需关心"选对下一层"。
四、一次请求的完整流转
以 GET http://localhost:8080/order/list 为例,请求在 Tomcat 内部的路径:
1. Coyote Acceptor 线程接受 TCP 连接
→ 放入 NIO Poller 的等待队列
2. Poller 检测到可读事件,唤醒工作线程(Executor 线程池)
3. 工作线程解析 HTTP 报文 → Http11InputBuffer
→ 生成 org.apache.catalina.connector.Request / Response(门面对象)
4. 进入 CoyoteAdapter.service()
→ 把协议 Request 转换为 ServletRequest
→ 交给 Engine 容器的 Pipeline
5. Engine Valve → Host Valve(按 Host 匹配)
6. Host Valve → Context Valve(按 Context 路径匹配 /order)
7. Context Valve → Wrapper Valve(按 URL 映射找 Servlet)
8. 执行 FilterChain(应用过滤器)→ 调用 servlet.service()
9. 业务代码返回 → Response 沿原路返回 → Coyote 写回客户端其中第 3 步值得注意:Tomcat 同时维护了两套 Request/Response:
| 对象 | 所在模块 | 作用 |
|---|---|---|
org.apache.coyote.Request | Coyote | 纯协议层,持有字节缓冲、Socket 信息 |
org.apache.catalina.connector.Request | Catalina | Servlet 容器层,实现 HttpServletRequest |
RequestFacade | Catalina | 门面对象,暴露给应用,屏蔽内部方法 |
应用代码拿到的是 RequestFacade——它只暴露规范要求的方法,防止业务代码把内部 Request 强转后调用受限逻辑。
五、生命周期管理:Lifecycle 接口
Tomcat 的所有组件都实现了 Lifecycle 接口,状态机统一管理:
NEW → INITIALIZING → INITIALIZED
→ STARTING_PREP → STARTING → STARTED
→ STOPPING_PREP → STOPPING → STOPPED
→ DESTROYING → DESTROYEDpublic interface Lifecycle {
void init(); // 初始化:创建资源,不对外服务
void start(); // 启动:开始接受请求
void stop(); // 停止:优雅关闭,拒绝新请求
void destroy(); // 销毁:释放资源
}LifecycleBase 提供了模板实现:状态校验、事件触发、异常处理都在基类完成,子类只需实现 initInternal()、startInternal() 等钩子方法。容器启动是递归的——Server.start() 会触发 Service → Connector/Engine → Host → Context 逐级启动,这就是 catalina.sh start 之后 Tomcat 日志按层级打印启动信息的原因。
六、类加载层次与双亲委派
Tomcat 的类加载器层次(与 Java 默认双亲委派模型的差异)是架构中重要的一环:
Bootstrap ClassLoader ← JDK 自带(rt.jar / java.base)
└── System ClassLoader ← catalina.bat 指定的 classpath
└── Common ClassLoader ← conf/catalina.properties 的 common.loader
├── Catalina ClassLoader ← 容器自身(catalina 包)
├── Shared ClassLoader ← 所有应用共享
│ └── Webapp ClassLoader ← 每个 Web 应用独立(app1 / app2 各一份)
└── Jasper ClassLoader ← JSP 编译后的类Web 应用之间通过 WebappClassLoader 实现隔离:应用 A 的类不会污染应用 B。WebappClassLoader 默认打破双亲委派——优先加载 WEB-INF/classes 与 WEB-INF/lib 下的类,找不到才委托给父加载器。
关于类加载机制的细节(打破双亲委派的具体规则、JSP 热编译等),将在"Tomcat 类加载机制详解"一篇中展开。
七、HTTP 协议支持与连接器演进
Coyote 从 Tomcat 8.5 开始默认使用 NIO 连接器,各代连接器对比:
| 连接器 | 线程模型 | IO 模型 | 适用场景 |
|---|---|---|---|
| BIO(8.5 移除) | 一线程一连接 | 阻塞 | 低并发 |
| NIO | 少量线程 + Poller | 非阻塞 | 默认选择 |
| NIO2 | 少量线程 + CompletionHandler | 异步 | 高并发长连接 |
| APR | 原生库 | 操作系统级 | 追求极致性能(需编译 native) |
Tomcat 9+ 默认使用 NIO 连接器,线程模型为"Acceptor 线程 + Poller 线程 + 工作线程池",后续在"连接器详解"一篇中详细展开。
八、Jasper 与 JSP
Jasper 是 JSP 引擎:JSP 文件首次被访问时,由 Jasper 编译成 Servlet 字节码(org.apache.jasper.servlet.JspServlet 触发),生成 .java 与 .class 文件存放在 work/Catalina/localhost/<app>/org/apache/jsp/ 目录。编译产物可以被 JVM 类加载器加载,后续请求直接执行编译后的 Servlet,这就是 JSP 首次访问慢、之后快的原因。
Jasper 的关键机制:
- 预编译:启动时通过
JspC批量编译,避免首访抖动 - 热更新:JSP 文件修改时间变化后,
JspServlet检测到并重新编译 - 页面指令:
<%@ page %>、<%@ include %>、<%@ taglib %>在编译期处理
九、常见部署形态
| 形态 | 说明 | 适用场景 |
|---|---|---|
| 独立部署 | Tomcat 独立进程,WAR 放入 webapps | 传统单体应用 |
| 内嵌模式 | Spring Boot 内嵌 Tomcat,Jar 直接运行 | 微服务、云原生 |
| 反向代理后置 | Nginx/Apache 前置,AJP 或 HTTP 转发 | 高并发站点 |
| 集群 | 多实例 + 会话复制/Redis 共享会话 | 高可用、横向扩展 |
Spring Boot 内嵌的 Tomcat 只是将上面的组件树用代码装配(Tomcat 类 + Connector + StandardEngine),运行机制与独立部署完全一致。
十、架构设计启示
Tomcat 的架构设计值得借鉴:
- 协议与容器解耦:Coyote 与 Catalina 之间通过门面对象通信,使得 HTTP/1.1、HTTP/2、AJP 可以无缝切换,新增协议只需扩展 Coyote 层。
- 层级容器 + 责任链:Engine/Host/Context/Wrapper 四层结构配合 Pipeline-Valve,让不同粒度的拦截逻辑各归其位。
- 统一生命周期:所有组件遵循同一状态机,启动顺序、优雅停止、资源回收都有确定性保障。
- 类加载隔离:WebappClassLoader 保证多应用共存时互不干扰,这是 Java 应用服务器多租户能力的基石。
参考链接: