第 1 章:Tomcat Engine 的定义与定位
1.1 什么是 Engine?
在 Tomcat 的核心架构中,Engine 是整个 Servlet 容器的"心脏" ,负责接收 Connector
传递过来的请求,并将其路由到合适的 Host → Context → Wrapper
,最终交给具体的 Servlet
执行。
可以把它类比成高速公路的交通枢纽:
-
Connector
像是高速路的入口收费站,负责接收外部车辆(请求)。 -
Engine
是高速路的主干线,决定车辆应该走哪一条支路。 -
Host
是具体的服务区,相当于一个虚拟主机(domain)。 -
Context
是某个服务区里的商场(web 应用)。 -
Wrapper
就是商场里的具体商铺(Servlet)。
1.2 Engine 的地位
在 Tomcat 的 Catalina 容器分层模型中:
-
Server
:Tomcat 的顶层容器,代表整个服务器。 -
Service
:一个服务,可以包含多个Connector
和一个Engine
。 -
Engine
:核心请求分发容器(每个 Service 仅有一个 Engine)。 -
Host
:虚拟主机,支持多域名部署。 -
Context
:Web 应用容器,一个 Host 下可以有多个 Context。 -
Wrapper
:最底层,封装具体的 Servlet。
📌 重点:一个 Service 只能绑定一个 Engine ,但 Engine 内部可以管理多个 Host,从而实现多站点、多租户。
1.3 配置示例
在 server.xml
中,我们能直观地看到 Engine 的定义:
<Service name="Catalina">
<!-- Connector 定义省略 -->
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT" reloadable="true" />
</Host>
</Engine>
</Service>
解析:
-
Engine
绑定到 Service(如 Catalina Service)。 -
defaultHost="localhost"
指定默认虚拟主机。 -
Host
定义了一个虚拟主机,可绑定多个 Web 应用(Context)。
1.4 源码定位
在 Tomcat 10.x 源码中:
-
org.apache.catalina.Engine
是一个接口,继承自Container
。 -
默认实现类为
org.apache.catalina.core.StandardEngine
。public interface Engine extends Container { // 获取默认Host public String getDefaultHost(); // 设置默认Host public void setDefaultHost(String defaultHost); }
而 StandardEngine
则实现了核心逻辑:
public class StandardEngine extends ContainerBase implements Engine {
private String defaultHost = null;
@Override
public String getDefaultHost() {
return (this.defaultHost);
}
@Override
public void setDefaultHost(String host) {
this.defaultHost = host;
}
}
可以看到:
-
Engine 其实就是一个 特殊的 Container ,它的子容器是
Host
。 -
它的关键属性就是
defaultHost
。
1.5 实际应用场景
假设你要在同一台 Tomcat 部署多个站点:
<Engine name="Catalina" defaultHost="www.example.com">
<Host name="www.example.com" appBase="site1" />
<Host name="blog.example.com" appBase="site2" />
</Engine>
这样,访问:
-
http://www.example.com/
→ 进入 site1 -
http://blog.example.com/
→ 进入 site2
👉 Engine 在这里就扮演了 多租户隔离管理者 的角色。
✅ 小结:
-
Engine 是 Service 内的核心容器,负责将请求分发到正确的 Host/Context/Wrapper。
-
它是 Servlet 容器分层结构的中枢,既是容器(可以包含 Host),又是路由器(决定请求走向)。
-
配置层面上,Engine 通常只需要指定
defaultHost
,但在多站点、多域名部署时会非常关键。
第 2 章:Engine 在 Tomcat 分层架构中的作用
2.1 Tomcat 的容器分层模型
Tomcat 的核心是 Catalina 容器 ,它采用了分层容器架构(Container Hierarchy),从上到下依次是:
-
Server(顶层,代表整个 Tomcat 实例)
-
Service(一个服务,包含一个 Engine 和多个 Connector)
-
Engine(请求分发枢纽,一个 Service 只能有一个 Engine)
-
Host(虚拟主机,代表一个域名站点)
-
Context(Web 应用,一个 Host 下可以有多个 Context)
-
Wrapper(最底层,封装具体的 Servlet)
👉 这种分层设计,使 Tomcat 容器既清晰又灵活,方便扩展。
2.2 Engine 与其他组件的关系
-
与 Server 的关系
-
Server
是最顶层,包含多个Service
。 -
每个
Service
必须有一个Engine
,因此 Engine 相当于 Server 与 Connector 的桥梁。
-
-
与 Service 的关系
-
Service
管理一组Connector
(负责协议/网络)和一个Engine
(负责容器)。 -
请求流向:
Connector → Engine
。
-
-
与 Host 的关系
-
Engine 的直接子容器就是 Host。
-
一个 Engine 可以有多个 Host,用来支持多域名。
-
Host 内部再细分为 Context → Wrapper。
-
2.3 请求流转路径
结合一次 HTTP 请求来理解 Engine 的作用。假设用户访问:
http://blog.example.com/app/hello
请求流转大致如下:
-
Connector (如
Http11NioProtocol
)接收到请求。 -
请求被传递到 Engine。
-
Engine 根据请求头中的
Host: blog.example.com
,找到对应的Host
。 -
在
Host
内部,根据app
定位到Context
(Web 应用)。 -
在
Context
内部,Mapper
会找到/hello
对应的Wrapper
(Servlet)。 -
最终调用 Servlet 的
service()
方法执行逻辑。
📌 Engine 在这里的作用就是 承上启下:
-
上承 Connector 的网络输入
-
下启 Host/Context/Wrapper 的业务容器
2.4 源码结构剖析
在源码层面,Engine 继承自 Container
接口,而 Container
是所有容器(Engine/Host/Context/Wrapper)的顶层接口。
public interface Container {
public String getName();
public void setName(String name);
public Container getParent();
public void setParent(Container container);
public Container[] findChildren();
}
而 Engine
是一个更具体的容器:
public interface Engine extends Container {
public String getDefaultHost();
public void setDefaultHost(String defaultHost);
}
👉 可以看出,Engine 的特殊性在于:
-
它的子容器是 Host
-
它必须有一个默认 Host
2.5 配置中的层级体现
来看一个 server.xml
的简化片段:
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"/>
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps" />
</Engine>
</Service>
</Server>
层次结构非常清晰:
-
Server → Service → Engine → Host
-
Connector 独立于容器,但它们和 Engine 一起组成 Service
2.6 类比理解
如果把 Tomcat 类比成一个"快递公司":
-
Server:快递公司总部
-
Service:一个分公司(有自己的运输队和仓库)
-
Connector:运输队,负责接收外部快递
-
Engine:分公司总仓库,负责快递分拣
-
Host:城市分仓(北京、上海...)
-
Context:区县分仓(海淀区、浦东新区...)
-
Wrapper:具体的投递员(Servlet)
在这个比喻里,Engine 就是大仓库的分拣系统,决定一个快递(请求)应该送到哪一个城市分仓(Host)。
✅ 小结
-
Engine 在分层架构中处于 Service 与 Host 之间,是连接网络层(Connector)与应用层(Context/Wrapper)的关键枢纽。
-
Engine 的唯一性(每个 Service 只能有一个 Engine)保证了请求路由的稳定性。
-
它的本质作用是 请求分发和容器管理,承载了整个 Tomcat 容器的核心逻辑。
第 3 章:Engine 的生命周期管理(初始化、启动、停止)
3.1 生命周期接口
Tomcat 中所有容器组件(Engine、Host、Context、Wrapper)都实现了 Lifecycle
接口,用来统一管理组件的状态转换。
public interface Lifecycle {
String BEFORE_INIT_EVENT = "before_init";
String AFTER_INIT_EVENT = "after_init";
String START_EVENT = "start";
String BEFORE_START_EVENT = "before_start";
String AFTER_START_EVENT = "after_start";
String STOP_EVENT = "stop";
String BEFORE_STOP_EVENT = "before_stop";
String AFTER_STOP_EVENT = "after_stop";
String DESTROY_EVENT = "destroy";
void init() throws LifecycleException;
void start() throws LifecycleException;
void stop() throws LifecycleException;
void destroy() throws LifecycleException;
void addLifecycleListener(LifecycleListener listener);
}
📌 关键点:
-
每个阶段都会触发相应的事件(Event),供监听器(Listener)扩展。
-
Engine
作为ContainerBase
的子类,继承了Lifecycle
的完整实现。
3.2 生命周期阶段详解
Engine 生命周期主要分为以下几个阶段:
-
初始化(init)
-
初始化容器结构(加载子容器 Host)。
-
准备 Mapper(请求路径到容器的映射器)。
-
通知所有注册的
LifecycleListener
。
-
-
启动(start)
-
调用
startInternal()
,启动自身及子容器(Host → Context → Wrapper)。 -
启动 Pipeline(责任链)中的 Valve。
-
触发
START_EVENT
。
-
-
停止(stop)
-
调用
stopInternal()
,依次停止 Pipeline 和子容器。 -
回收资源(线程池、缓存、Session 管理器等)。
-
触发
STOP_EVENT
。
-
-
销毁(destroy)
-
释放底层资源。
-
通知所有监听器。
-
3.3 源码级解析(Tomcat 10.x)
以 StandardEngine
为例(继承自 ContainerBase
):
@Override
protected void startInternal() throws LifecycleException {
// 设置状态为 STARTING
setState(LifecycleState.STARTING);
// 启动子容器(Host)
super.startInternal();
}
可以看到,StandardEngine
并没有复杂逻辑,核心都在父类 ContainerBase
。
在 ContainerBase.startInternal()
中:
@Override
protected synchronized void startInternal() throws LifecycleException {
// 启动 Pipeline(责任链)
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 启动所有子容器(Host)
for (Container child : findChildren()) {
child.start();
}
setState(LifecycleState.STARTED);
}
👉 逻辑很清晰:
-
先启动 Pipeline(请求处理链)。
-
再递归启动所有子容器(Host → Context → Wrapper)。
同理,stopInternal()
过程正好反向:
-
先停止子容器
-
再停止 Pipeline
-
设置状态为 STOPPED
3.4 配置与生命周期的关系
在 server.xml
中修改 Engine 配置时,通常需要 重启 Tomcat 才能生效。原因就是 Engine 的生命周期被绑定到 Service → Server 的生命周期。
例如:
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps" />
</Engine>
如果修改了 defaultHost
,必须重启 Tomcat,因为 Engine.startInternal()
只会在 Service 启动时执行一次。
3.5 生命周期监听器(Listener)
我们可以通过 LifecycleListener
扩展 Engine 的生命周期行为。
示例:自定义一个日志监听器
public class EngineLifecycleLogger implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
System.out.println("Engine event: " + event.getType());
}
}
在 server.xml
中挂载:
<Engine name="Catalina" defaultHost="localhost">
<Listener className="com.example.EngineLifecycleLogger"/>
<Host name="localhost" appBase="webapps" />
</Engine>
这样在 Engine 初始化/启动/停止时都会输出日志。
3.6 实际应用场景
-
多站点动态部署
如果需要在运行中动态添加新的
Host
,必须确保 Engine 已经start
,否则 Host 无法挂载。 -
灰度发布
通过 LifecycleListener,可以在 Engine 启动时注册动态配置(如加载灰度路由规则)。
-
资源回收
在
stop
或destroy
阶段,可以自定义 Listener 释放数据库连接池、关闭 MQ 客户端等资源。
3.7 常见问题与排查
-
Engine 未启动导致 404
-
现象:Connector 正常,但所有请求返回 404。
-
原因:Engine 或子容器未成功启动(可能是
web.xml
配置错误)。 -
解决:检查
catalina.out
,确认Engine.startInternal()
是否报错。
-
-
资源未释放导致内存泄漏
-
现象:Tomcat 重启后内存不断升高。
-
原因:Engine 停止时未正确调用
destroy()
,Listener 或线程池未释放。 -
解决:实现自定义
LifecycleListener
,在 STOP_EVENT 阶段手动清理资源。
-
第 4 章:Host/Context/Wrapper 容器与 Engine 的关系
4.1 容器层级回顾
在 Tomcat 的容器分层模型中,Engine 作为 Service 内的核心容器 ,其子容器就是 Host。层级关系如下:
Engine
└── Host (虚拟主机)
└── Context (Web 应用)
└── Wrapper (具体 Servlet)
👉 每一层容器都实现了 Container
接口,因此它们在管理子容器、查找子容器方面保持一致性。
4.2 Engine 与 Host 的关系
-
Engine 的子容器是 Host
-
每个 Host 对应一个虚拟主机(即域名),支持多站点部署。
-
Engine 通过
defaultHost
属性指定默认 Host,用来处理无法匹配的请求。
-
-
源码解析(Engine 添加 Host)
在
ContainerBase
中,所有子容器统一管理:@Override public void addChild(Container child) { child.setParent(this); // 设置父容器 children.put(child.getName(), child); // 存入子容器集合 }
对于
StandardEngine
来说,这里的 child 就是Host
。 -
配置示例
<Engine name="Catalina" defaultHost="www.example.com"> <Host name="www.example.com" appBase="site1"/> <Host name="blog.example.com" appBase="site2"/> </Engine>
-
www.example.com
→ 映射到 site1 应用目录 -
blog.example.com
→ 映射到 site2 应用目录
-
4.3 Host 与 Context 的关系
-
Host 的子容器是 Context
-
每个 Context 对应一个 Web 应用(通常是一个
WAR
包)。 -
Host 负责应用的自动部署(
autoDeploy
、unpackWARs
参数控制)。
-
-
配置示例
<Host name="localhost" appBase="webapps" autoDeploy="true"> <Context path="/app1" docBase="app1"/> <Context path="/app2" docBase="app2"/> </Host>
-
http://localhost:8080/app1
→ 访问 app1 应用 -
http://localhost:8080/app2
→ 访问 app2 应用
-
-
源码解析
StandardHost
继承ContainerBase
,其addChild()
方法确保子容器是Context
:@Override public void addChild(Container child) { if (!(child instanceof Context)) { throw new IllegalArgumentException("Child not a Context"); } super.addChild(child); }
4.4 Context 与 Wrapper 的关系
-
Context 的子容器是 Wrapper
-
Context 表示一个 Web 应用,对应
web.xml
中的配置。 -
Wrapper 封装了具体的 Servlet。
-
-
配置来源
在
web.xml
或@WebServlet
注解中定义的 Servlet,都会被解析成 Wrapper:<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.example.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
源码解析(Context 添加 Wrapper)
在
StandardContext
中:@Override public void addChild(Container child) { if (!(child instanceof Wrapper)) { throw new IllegalArgumentException("Child not a Wrapper"); } super.addChild(child); }
👉 这里的
Wrapper
就是对HelloServlet
的封装。
4.5 请求逐级下发机制
假设用户访问:
http://blog.example.com/app1/hello
请求路径在容器层级中的映射过程是:
-
Engine
- 根据
Host
头(blog.example.com
)找到对应的 Host。
- 根据
-
Host
- 根据 URL 的 Context Path(
/app1
)找到对应的 Context。
- 根据 URL 的 Context Path(
-
Context
- 根据 Servlet 映射(
/hello
)找到对应的 Wrapper。
- 根据 Servlet 映射(
-
Wrapper
- 调用目标 Servlet 的
service()
方法处理请求。
- 调用目标 Servlet 的
📌 这里的 Mapper 组件起到关键作用,它在 Engine 层面维护了 Host → Context → Wrapper 的映射关系。
4.6 类比理解
我们继续用"快递公司"的类比:
-
Engine:总仓库(总分拣中心)
-
Host:城市仓库(北京仓、上海仓)
-
Context:区县仓库(海淀区仓、浦东区仓)
-
Wrapper:快递员(负责具体投递)
👉 请求就像一件快递,在 Engine 的大仓库里首先按城市分拣(Host),再按区县分拣(Context),最后分配给具体快递员(Wrapper)。
第 5 章:责任链模式(Pipeline-Valve)在 Engine 中的实现
5.1 责任链模式简介
责任链模式(Chain of Responsibility)是一种 行为设计模式,允许请求沿着处理链传递,直到某个处理器处理它。
在 Tomcat 中:
-
Pipeline → 责任链的容器
-
Valve → 责任链中的节点,每个 Valve 可以处理请求或将请求传递给下一个 Valve
优势:
-
灵活可扩展
-
每个处理器独立,实现关注点分离
-
支持动态添加/删除处理节点
5.2 Pipeline 与 Valve 的定义
-
Pipeline 接口
public interface Pipeline { public Valve getBasic(); public void setBasic(Valve valve); public void addValve(Valve valve); public void invoke(Request request, Response response) throws IOException, ServletException; }
-
Valve 接口
public interface Valve { public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException; }
📌 核心思想:
-
Pipeline 维护一个 Valve 链
-
每个 Valve 调用
context.invokeNext()
将请求传递给下一个 Valve -
最后执行 Basic Valve(通常是 Servlet 调用)
5.3 Engine 的 Pipeline 实现
在 Tomcat 中,Engine、Host、Context 都实现了 Pipeline。
public class StandardEngine extends ContainerBase implements Engine, Pipeline {
protected Valve basic = null;
protected ArrayList<Valve> valves = new ArrayList<>();
@Override
public void addValve(Valve valve) {
valves.add(valve);
((ContaineBase) this).pipeline = this;
}
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {
((ValveContextImpl) getPipelineContext()).invokeNext(request, response);
}
}
-
valves:普通处理节点
-
basic:基础处理节点(最终处理,如将请求交给子容器)
5.4 Pipeline 执行流程
-
Connector 将请求传递给 Engine
-
Engine 的 Pipeline 调用第一个 Valve
-
Valve 执行自己的逻辑(如安全检查、日志记录、过滤器链等)
-
调用
context.invokeNext()
→ 下一个 Valve -
最终调用 Basic Valve → 将请求传递给 Host → Context → Wrapper → Servlet
5.5 配置示例
在 server.xml
中添加自定义 Valve:
<Engine name="Catalina" defaultHost="localhost">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%h %l %u %t "%r" %s %b"/>
<Host name="localhost" appBase="webapps"/>
</Engine>
-
AccessLogValve
会在请求结束后记录日志 -
可以自定义多个 Valve,形成责任链
5.6 自定义 Valve 示例
假设我们需要实现请求审计(打印请求 URL 与处理时间):
public class AuditValve implements Valve {
@Override
public void invoke(Request request, Response response, ValveContext context)
throws IOException, ServletException {
long start = System.currentTimeMillis();
System.out.println("Audit start: " + request.getRequestURI());
// 调用下一个 Valve
context.invokeNext(request, response);
long duration = System.currentTimeMillis() - start;
System.out.println("Audit end: " + request.getRequestURI() + ", duration=" + duration + "ms");
}
}
-
添加到 Engine:
Engine engine = (Engine) service.getContainer(); engine.addValve(new AuditValve());
效果:每个请求都会经过 AuditValve 记录日志,然后传递给下一个 Valve。
5.7 Engine Pipeline 与子容器协作
-
Engine Pipeline 只处理 全局逻辑(如日志、安全、负载均衡)
-
Host/Context 的 Pipeline 处理 局部逻辑(如某个应用的过滤器、认证)
-
责任链是递归执行的:
- Engine → Host → Context → Wrapper → Servlet
-
调用顺序举例:
Engine Valve1 → Engine BasicValve → Host Valve2 → Host BasicValve → Context Valve3 → Context BasicValve → Wrapper → Servlet
5.8 类比理解
继续用"快递公司"类比:
-
Engine:总分拣中心
-
Valve1:检查包裹重量
-
Valve2:检查地址有效性
-
-
Host:城市仓库
- Valve:检查城市路由
-
Context:区县仓库
- Valve:检查快递员可用性
-
Wrapper:快递员
- 实际投递
每个环节都是责任链节点,包裹沿链向下流转,最终送达目的地。
第 6 章:请求处理流程(Mapper 定位 → Pipeline 执行 → Servlet 调用)
6.1 请求处理的总体流程
当外部请求进入 Tomcat 时,处理流程大致分为三步:
-
Mapper 定位
- Engine 根据请求的 Host 和 URI 找到对应的 Host/Context/Wrapper。
-
Pipeline 执行
- 请求沿着 Engine → Host → Context 的 Valve 链执行。
-
Servlet 调用
- Wrapper 调用目标 Servlet 的
service()
方法,完成业务逻辑处理。
- Wrapper 调用目标 Servlet 的
整个流程可概括为:
Connector → Engine (Pipeline) → Host → Context → Wrapper → Servlet
6.2 Connector 接收请求
以 Http11NioProtocol
为例:
SocketChannel channel = serverSocket.accept();
Request request = new Request(channel);
Response response = new Response(channel);
processor.process(request, response);
-
Connector 将网络请求封装成 Request 和 Response 对象。
-
请求被传递给 Engine 的 Pipeline 进行处理。
6.3 Engine Mapper 定位
Engine 内部有 Mapper 组件,用于将请求映射到对应的 Host/Context。
源码简化示例(StandardEngine
):
Host host = findHost(request.getServerName());
Context context = host.findChild(request.getContextPath());
Wrapper wrapper = context.findChild(request.getServletPath());
-
findHost()
:根据请求 Host 头找到对应的虚拟主机 -
findChild()
:在 Host/Context 中查找对应子容器
📌 核心:Engine 只负责找到 Host,Context 和 Wrapper 进一步处理 URL 映射。
6.4 Pipeline 执行
请求定位完成后,通过 Pipeline 执行 Valve 链:
engine.getPipeline().invoke(request, response);
执行逻辑:
-
Engine Valve1 → Engine Valve2 → Engine BasicValve
-
Host Valve → Host BasicValve
-
Context Valve → Context BasicValve → Wrapper.invoke()
-
Engine BasicValve:将请求传给 Host
-
Host BasicValve:将请求传给 Context
-
Context BasicValve:将请求传给 Wrapper(Servlet)
6.5 Wrapper 调用 Servlet
Wrapper 是最底层的容器,封装了 Servlet 实例:
public void invoke(Request request, Response response) throws ServletException, IOException {
Servlet servlet = loadServlet();
servlet.service(request.getRequest(), response.getResponse());
}
-
loadServlet():延迟加载 Servlet,首次访问时实例化
-
service() :调用具体的
doGet()
或doPost()
方法处理请求
6.6 文字版完整流程描述
假设用户请求 URL:
http://blog.example.com/app1/hello
流程如下:
-
Connector 接收 TCP 连接,封装 Request/Response
-
Engine Mapper 查找 Host:
blog.example.com
-
Engine Pipeline 执行全局 Valve(如日志、审计)
-
Host Mapper 查找 Context:
/app1
-
Host Pipeline 执行局部 Valve(如安全检查)
-
Context Mapper 查找 Wrapper:
/hello → HelloServlet
-
Context Pipeline 执行应用级 Valve(如请求计时)
-
Wrapper 调用 Servlet
service()
方法 -
Servlet 处理业务逻辑,写入 Response
-
Response 返回给 Connector → 返回客户端
6.7 代码片段演示(模拟流程)
// Engine 层
engine.getPipeline().invoke(request, response);
// Engine BasicValve
Host host = engine.findHost(request.getServerName());
host.getPipeline().invoke(request, response);
// Host BasicValve
Context context = host.findChild(request.getContextPath());
context.getPipeline().invoke(request, response);
// Context BasicValve
Wrapper wrapper = context.findChild(request.getServletPath());
wrapper.invoke(request, response);
// Wrapper
Servlet servlet = wrapper.loadServlet();
servlet.service(request.getRequest(), response.getResponse());
这个流程展示了 Mapper 定位 + Pipeline 执行 + Servlet 调用 的逐级处理逻辑。
6.8 实际应用场景
-
多域名部署
- Engine Mapper 根据 Host 头路由到不同虚拟主机
-
应用隔离
- Host/Context 层提供独立 Pipeline 和 Wrapper,保证应用隔离
-
全局监控
- Engine Pipeline 可以记录所有请求日志,Host/Context Pipeline 可做性能分析
6.9 小结
-
Engine 负责请求的 路由和全局处理
-
Host/Context 提供 局部处理和应用隔离
-
Wrapper 执行 具体业务逻辑
-
Mapper + Pipeline 是 Tomcat 请求处理链的核心机制
-
通过源码和文字描述,可以清晰理解请求从 TCP 到 Servlet 的完整路径
第 7 章:Engine 与 Servlet 规范的兼容性
7.1 Servlet 规范概览
Servlet 规范定义了 Java Web 应用的核心接口和行为,包括:
-
Servlet 接口 :
javax.servlet.Servlet
或jakarta.servlet.Servlet
- 方法:
init() → service() → destroy()
- 方法:
-
生命周期管理
- 容器负责创建 Servlet 实例、调用初始化方法、调用
service()
方法处理请求、最终销毁
- 容器负责创建 Servlet 实例、调用初始化方法、调用
-
请求/响应对象
ServletRequest
/ServletResponse
提供 HTTP 请求信息和响应机制
-
过滤器链(Filter)
- 支持请求预处理和后处理
-
监听器(Listener)
- 支持事件感知(如 Session 创建、Context 初始化)
Tomcat Engine 必须支持这些规范,才能保证 Web 应用的可移植性。
7.2 Engine 如何支持 Servlet 生命周期
Engine 通过 Wrapper 承载具体 Servlet,每个 Wrapper 的行为严格遵循 Servlet 规范:
public void invoke(Request request, Response response)
throws ServletException, IOException {
Servlet servlet = loadServlet(); // 创建或获取 Servlet 实例
servlet.service(request.getRequest(), response.getResponse()); // 调用 service
}
-
loadServlet()
:保证延迟加载,符合 Servlet 生命周期规范 -
Engine/Context/Wrapper 负责管理 Servlet 的 初始化、并发访问、销毁
7.3 请求与响应对象兼容
Tomcat Engine 内部的 Request
和 Response
对象封装了 HTTP 协议细节 ,同时实现了 ServletRequest/ServletResponse 接口:
public class RequestFacade implements ServletRequest {
private Request request; // 实际请求对象
}
-
通过 Facade 模式保护内部实现,保证 Web 应用无法直接访问底层容器
-
支持 Servlet 5.0 新特性,如 HTTP/2 协议、异步处理(
AsyncContext
)
7.4 Pipeline 与 Filter 机制的协作
-
Pipeline 是 Tomcat 的容器级责任链
-
Filter 是 Servlet 规范的应用级责任链
Engine 将请求处理分为两级:
-
Engine/Host/Context Pipeline:全局/局部容器逻辑
-
Filter Chain(Context → Wrapper):应用逻辑(如安全、日志、压缩)
示意流程:
Engine Pipeline → Host Pipeline → Context Pipeline → Filter Chain → Servlet.service()
- 这样既保证了容器级别功能,又兼容标准 Servlet 应用
7.5 支持异步请求(AsyncContext)
Servlet 3.0 及以上规范支持异步请求,Engine 在处理异步请求时也能兼容:
AsyncContext asyncContext = request.startAsync();
asyncContext.start(() -> {
servlet.service(request, response);
});
-
Engine 不会阻塞请求线程
-
Pipeline 继续处理其他请求,提高并发能力
7.6 支持 Servlet 注解和 web.xml 配置
Engine 配置的 Context 会解析 Servlet 注解(@WebServlet、@WebFilter) 或 web.xml 文件,自动生成对应 Wrapper/Filter:
Context context = host.findChild("/app1");
context.addChild(new StandardWrapper("HelloServlet", "com.example.HelloServlet"));
-
自动完成 URL 映射
-
支持多 Servlet/Filter/Listener,保证与规范一致
第 8 章:Engine 的扩展性(自定义 Valve/Listener)
8.1 Engine 的扩展点概览
Tomcat Engine 提供了两个主要扩展点:
-
Valve(责任链节点)
-
插入请求处理责任链
-
可执行全局或应用级逻辑
-
示例:日志记录、权限校验、性能监控
-
-
Listener(事件监听器)
-
监听 Engine 生命周期事件
-
可执行初始化、销毁、启动、停止等操作
-
示例:资源初始化、动态配置加载
-
8.2 自定义 Valve
8.2.1 Valve 接口
public interface Valve {
void invoke(Request request, Response response, ValveContext context)
throws IOException, ServletException;
}
8.2.2 自定义示例:请求计时
public class TimingValve implements Valve {
@Override
public void invoke(Request request, Response response, ValveContext context)
throws IOException, ServletException {
long start = System.currentTimeMillis();
context.invokeNext(request, response); // 调用下一个 Valve
long duration = System.currentTimeMillis() - start;
System.out.println("Request URI: " + request.getRequestURI() + ", Duration: " + duration + "ms");
}
}
8.2.3 添加到 Engine
Engine engine = (Engine) service.getContainer();
engine.addValve(new TimingValve());
-
执行顺序 :按照
addValve
的顺序形成责任链 -
Basic Valve:最后执行,将请求传给 Host
8.3 自定义 Listener
8.3.1 LifecycleListener 接口
public interface LifecycleListener {
void lifecycleEvent(LifecycleEvent event);
}
8.3.2 示例:Engine 启动日志
public class EngineStartupLogger implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
System.out.println("Engine started at " + System.currentTimeMillis());
}
}
}
8.3.3 在 server.xml 中配置
<Engine name="Catalina" defaultHost="localhost">
<Listener className="com.example.EngineStartupLogger"/>
<Host name="localhost" appBase="webapps"/>
</Engine>
- 当 Engine 启动时,会自动触发
lifecycleEvent
,输出启动日志