Tomcat 请求处理流程源码分析
概述
一次 HTTP 请求从网卡到 Servlet 业务代码,中间经过 Coyote(通信层)与 Catalina(容器层)两大部分。本文按源码执行路径逐层追踪:Endpoint 接收连接 → Processor 解析报文 → Adapter 桥接 → Engine/Host/Context 逐级分发 → FilterChain 与 Servlet,把抽象架构落到具体类与方法。
一、请求处理整体时序
Socket 到达
│
▼
1. Acceptor.run() 接收连接
▼
2. Poller.run() / Selector NIO 事件就绪
▼
3. SocketProcessor.doRun() 工作线程开始处理
▼
4. Http11Processor.service() 解析 HTTP 报文 → coyote Request/Response
▼
5. CoyoteAdapter.service() 协议对象 → 容器对象(桥接)
▼
6. Engine 容器链 Valve 逐级传递
▼
7. Context 的 FilterChain Servlet 过滤器链
▼
8. Wrapper → servlet.service() 业务代码二、连接层:Endpoint 与 Processor
2.1 Acceptor:接收连接
// org.apache.tomcat.util.net.AbstractEndpoint.Acceptor
// NioEndpoint 内部类,独立线程循环 accept
public void run() {
while (!stopCalled) {
SocketChannel socket = null;
try {
// 1. 阻塞接收新连接(accept)
socket = serverSock.accept();
...
// 2. 配置 socket(TCP_NODELAY 等)
// 3. 注册到 Poller
if (running && !poller.register(channel, socketWrapper)) {
// 注册失败则关闭
}
} catch (Exception e) {
// accept 异常处理:避免热点死循环,短暂休眠后继续
}
}
}Acceptor 是单线程循环,职责只有"接收并登记连接",绝不参与业务处理。
2.2 Poller:事件检测
// NioEndpoint.Poller —— Selector 多路复用
public boolean events() {
int keyCount = 0;
try {
keyCount = selector.select(); // 阻塞等待就绪事件
} catch (IOException x) {
return false;
}
...
// 遍历就绪的 SelectionKey
Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
...
// 可读事件 → 交给工作线程处理
if (sk.isReadable() || sk.isWritable()) {
processKey(sk, socketWrapper); // 内部创建 SocketProcessor 提交给 Executor
}
}
return true;
}Poller 把"就绪的连接"变成"待处理的任务",提交给工作线程池。
2.3 SocketProcessor:工作线程入口
// NioEndpoint.SocketProcessor —— 实现 Runnable,由 Executor 调度
public void doRun() {
...
if (handshake == null) {
// 调 Processor 处理
state = getHandler().process(socketWrapper, event);
}
}getHandler() 返回 Http11ConnectionHandler,它持有处理器缓存(每个连接一个 Http11Processor)。
三、协议层:Http11Processor
3.1 报文解析
// org.apache.coyote.http11.Http11Processor.service()
public void service(SocketWrapperBase<?> socketWrapper) throws IOException {
...
while (keepAlive && !error && !comet) {
// 1. 解析请求行与请求头
if (!inputBuffer.parseRequestLine(keptAlive)) { ... }
if (!inputBuffer.parseHeaders()) { ... }
// 2. 解析请求体(视 Content-Length / Transfer-Encoding)
inputBuffer.parseBody();
// 3. 触发容器处理(见下节 Adapter)
getAdapter().service(request, response);
...
}
}解析结果存入 org.apache.coyote.Request——包含请求行、Header、参数等协议层数据,此时还不是 Servlet 规范对象。
3.2 关键点:keep-alive 循环
while (keepAlive) 说明一个工作线程可以串行处理同一连接上的多个请求(HTTP keep-alive),减少线程创建开销——这也是 maxKeepAliveRequests 限制的意义。
四、桥接层:CoyoteAdapter
Coyote 的 Request 要转成 Catalina 的 org.apache.catalina.connector.Request(实现 HttpServletRequest),由 CoyoteAdapter.service() 完成:
// org.apache.catalina.connector.CoyoteAdapter.service()
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) {
// 1. 从请求对象池获取 Catalina Request/Response 包装
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
...
// 2. 关联 coyote 对象与 catalina 对象
request.setCoyoteRequest(req);
response.setCoyoteResponse(res);
// 3. 解析 URI、查询字符串、Host
request.getMappingData().parsePath(req.requestURI(), req.decodedURI());
...
// 4. 交给 Engine 容器链
connector.getService().getContainer().getPipeline()
.getFirst().invoke(request, response);
...
}这里完成了协议对象 → 容器对象的桥接,随后请求进入容器层。
五、容器层:Pipeline-Valve 链
5.1 四层容器的逐级传递
请求从 Engine 开始,每层容器的 Pipeline 依次执行:
StandardEngineValve(Engine 管道)
└─ StandardHostValve(Host 管道)
└─ StandardContextValve(Context 管道)
└─ StandardWrapperValve(Wrapper 管道)
└─ FilterChain → servlet5.2 EngineValve:定位 Host
// StandardEngineValve.invoke() 简化
public void invoke(Request request, Response response) {
// 根据 Host 头选择虚拟主机
Host host = request.getHost();
if (host == null) {
response.sendError(400); // 没有匹配的 Host
return;
}
// 进入 Host 管道
host.getPipeline().getFirst().invoke(request, response);
}5.3 HostValve:定位 Context
// StandardHostValve.invoke() 简化
public void invoke(Request request, Response response) {
Context context = request.getContext(); // 由 Mapper 预先匹配好
...
// 选择应用上下文
context.getPipeline().getFirst().invoke(request, response);
}request.getContext() 的结果其实在 Mapper 阶段就已算好——CoyoteAdapter.service() 中 parsePath 之后的 request.getMappingData() 包含 context、wrapper 的映射结果,Valve 只是取用。Mapper 在启动时构建 URL 模式 → Context/Wrapper 的索引,请求时快速查找。
5.4 ContextValve:进入应用
// StandardContextValve.invoke() 简化
public void invoke(Request request, Response response) {
// 找到目标 Servlet 包装器
Wrapper wrapper = request.getWrapper();
wrapper.getPipeline().getFirst().invoke(request, response);
}5.5 WrapperValve:创建 FilterChain 并调用 Servlet
// StandardWrapperValve.invoke() 关键逻辑
public void invoke(Request request, Response response) {
// 1. 分配(必要时初始化)Servlet 实例
servlet = wrapper.allocate();
// 2. 构建过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterChainFactory.createFilterChain(request, wrapper, servlet);
// 3. 触发监听器事件(begin / 请求进入)
// 4. 执行过滤器链
filterChain.doFilter(request.getRequest(), response.getResponse());
}六、FilterChain 与 Servlet 调用
6.1 过滤器链执行
// org.apache.catalina.core.ApplicationFilterChain.doFilter()
public void doFilter(ServletRequest request, ServletResponse response) {
// 依次执行注册的 Filter,最后调用 Servlet
internalDoFilter(request, response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
// 取下一个 Filter
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
// 递归/迭代式调用,形成链
filter.doFilter(request, response, this);
return;
}
// 所有 Filter 执行完毕 → 调用 Servlet
servlet.service(request, response);
}过滤器链是责任链模式的经典实现:每个 Filter 调用 chain.doFilter() 传向下一个,最后一个环节触发 servlet.service()。
6.2 Servlet 实例管理
StandardWrapper.allocate() 维护 Servlet 实例池:单例 Servlet 首次请求时 init(),之后复用。默认懒加载——Servlet 在首次请求才初始化,这也是"第一次访问慢"的原因之一(可在 web.xml 配置 load-on-startup 提前初始化)。
七、完整源码路径速查
| 阶段 | 关键类 | 核心方法 |
|---|---|---|
| 接收连接 | NioEndpoint$Acceptor | run() |
| 事件检测 | NioEndpoint$Poller | events() |
| 工作线程 | NioEndpoint$SocketProcessor | doRun() |
| 报文解析 | Http11Processor | service() |
| 协议→容器桥接 | CoyoteAdapter | service() |
| URL 映射 | Mapper | map() |
| Engine 分发 | StandardEngineValve | invoke() |
| Host 分发 | StandardHostValve | invoke() |
| Context 分发 | StandardContextValve | invoke() |
| Servlet 调用 | StandardWrapperValve | invoke() |
| 过滤器链 | ApplicationFilterChain | doFilter() |
八、请求线程与异步化
8.1 传统同步模型
工作线程 = 处理中的请求
线程阻塞在 servlet.service() 直到响应完成8.2 Servlet 3.1 异步
request.startAsync() 后,工作线程可提前释放,业务在异步线程完成后 complete():
AsyncContext asyncContext = request.startAsync();
// 业务提交到异步线程池
executor.submit(() -> {
// 耗时处理
asyncContext.getResponse().getWriter().write(result);
asyncContext.complete(); // 通知容器响应完成
});异步化让 Tomcat 工作线程不再被慢请求占满,提升长任务场景的吞吐。
九、常见问题定位
| 问题 | 从源码角度定位 |
|---|---|
| 请求卡住无响应 | jstack 看工作线程栈是否卡在业务代码(服务端调用/数据库) |
| 大量连接不释放 | 检查 keep-alive 与 maxKeepAliveRequests |
| 请求 400 | Mapper/解析阶段失败(URL 非法、参数超限) |
| 慢请求拖垮整体 | 定位是 WrapperValve 内业务耗时还是线程池排队 |
| 响应时间与业务不符 | 过滤链中某 Filter 耗时(日志耗时逐段核对) |
参考链接: