0%

tomcat之web请求处理

Tomcat Web 请求处理流程详解

Tomcat 作为 Servlet 容器,其核心功能是接收并处理客户端的 HTTP 请求。从请求进入服务器到响应返回客户端,涉及多个组件的协同工作。本文将详细解析 Tomcat 处理 Web 请求的完整流程,重点关注核心组件的交互逻辑。

请求处理总体流程

Tomcat 处理请求的入口是 org.apache.catalina.connector.CoyoteAdapter.service() 方法,该方法串联起从协议解析到 Servlet 执行的全流程。整体可分为以下步骤:

  1. 请求接收与协议适配
    客户端请求通过 Connector 进入 Tomcat,Connector 根据协议类型(如 HTTP/1.1、AJP)创建对应的 org.apache.coyote.Request(Coyote 请求对象)和 org.apache.coyote.Response(Coyote 响应对象),并转换为 Tomcat 内部的 org.apache.catalina.connector.Requestorg.apache.catalina.connector.Response
  2. 容器层级匹配
    • Engine:根据请求的主机名(如 localhost)匹配对应的虚拟主机(Host)。
    • Host:根据请求路径(如 /myapp)匹配对应的 Web 应用(Context)。
    • Context:通过 URL 映射找到对应的 Servlet(由 StandardWrapper 管理)。
  3. 请求处理与响应生成
    • 执行认证、授权等预处理。
    • 调用目标 Servlet 的 service() 方法处理请求。
    • 将处理结果通过 Response 对象封装,反向传递给客户端。
  4. 资源清理
    • 同步请求:关闭输入流和输出流。
    • 异步请求:触发 ReadListener 事件,处理剩余数据。

HTTP Connector 请求处理细节

Connector 是请求进入 Tomcat 的第一道关卡,负责底层网络通信和协议解析。以 HTTP 协议的 NIO 连接器为例,其处理流程如下:

1. 连接器启动与端口监听

当 Connector 启动时,会初始化并启动 Endpoint(如 NioEndpoint),主要包含以下组件:

  • Acceptor 线程:监听指定端口(如 8080),接收客户端的 Socket 连接。默认启动的 Acceptor 线程数由 acceptorThreadCount 配置(默认 1)。
  • Poller 线程:通过 NIO 的 Selector 监听 Socket 的 I/O 事件(读 / 写),实现非阻塞 I/O。
  • 线程池:由 maxThreads 等参数配置,用于处理已就绪的 Socket 连接。

核心代码片段(Acceptor 监听逻辑)

1
2
3
4
5
6
7
8
9
10
public void run() {
while (running) {
// 监听并接收 Socket 连接
SocketChannel socket = serverSock.accept();
// 将连接封装为 SocketWrapper,交给 Poller 处理
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
}
}

2. Socket 连接处理

Acceptor 接收 Socket 后,将其封装为 SocketWrapper,并通过 SocketProcessor 提交到线程池处理:

  • SocketProcessor:实现 Runnable 接口,负责将 Socket 交给 ConnectionHandler 处理。
  • ConnectionHandler:为连接选择合适的 Processor(如 Http11Processor),解析 HTTP 协议。

性能优化
Processor 会被缓存和复用 —— 同一连接的后续请求可复用同一个 Processor,连接关闭后 Processor 会被回收至队列,减少对象创建开销。

3. 协议解析与请求封装

Processor 负责解析 HTTP 协议内容(请求行、请求头、请求体),并将解析结果填充到 CoyoteRequest 中,主要步骤包括:

  • 解析请求方法(GET/POST 等)、URI、HTTP 版本。
  • 解析请求头(如 HostContent-Type)。
  • 处理请求体(如表单数据、文件上传),并封装为参数。

解析完成后,CoyoteAdapterCoyoteRequest 转换为 CatalinaRequest(实现 ServletRequest 接口),进入容器处理阶段。

容器层级的请求路由

Tomcat 的容器层级为 Engine → Host → Context → Wrapper,每层容器负责不同的路由逻辑:

1. Engine 匹配 Host

Engine 作为顶层容器,管理多个 Host(虚拟主机),通过请求的 Host 头或 IP 地址匹配对应的 Host。例如:

  • 若请求头 Host: www.example.com,则匹配名称为 www.example.com 的 Host。
  • 若未匹配到,则使用默认 Host(通常为 localhost)。

2. Host 匹配 Context

Host 管理多个 Web 应用(Context),通过请求的 URI 路径匹配对应的 Context。例如:

  • 请求 http://localhost:8080/myapp/index.jsp 中,/myapp 对应 Context 的 path 属性。
  • 根路径 / 对应 path="" 的 Context(通常为 webapps/ROOT 目录)。

3. Context 映射 Servlet

Context 负责将请求 URI 映射到具体的 Servlet,由 org.apache.tomcat.util.http.mapper.Mapper 组件处理:

  • URL 转码与校验:对 URI 进行 UTF-8 解码(由 URIEncoding 配置),检查是否包含非法字符(如 \:),非法则返回 404。
  • 映射规则
    • 精确匹配:如 /hello 映射到 @WebServlet("/hello") 的 Servlet。
    • 路径匹配:如 /user/* 匹配所有以 /user/ 开头的请求。
    • 扩展名匹配:如 *.jsp 映射到 JspServlet。
  • 结果封装:映射结果存储在 MappingData 对象中,包含目标 Wrapper(Servlet 容器)、路径参数等信息。

特殊情况处理

  • 若映射结果需要重定向(如末尾缺少 /),则通过 Response.sendRedirect() 处理。
  • 若请求方法为 TRACE 且 Connector 禁用(allowTrace="false"),返回 405(方法不允许)。

Servlet 处理与响应返回

当请求定位到具体的 Servlet 后,Tomcat 执行以下步骤:

1. 预处理:认证与授权

  • 认证:根据 Context 配置的 Realm(如基于用户名密码、LDAP)验证用户身份。
  • 授权:检查用户是否有权限访问目标资源(基于 <security-constraint> 配置)。

2. 执行 Servlet 逻辑

  • 实例化 Servlet:若 Servlet 未初始化(loadOnStartup >= 0 时会在 Context 启动时初始化),则调用 Servlet.init() 方法。
  • 调用 service 方法:根据请求方法(GET/POST 等)调用对应的 doGet()doPost() 等方法。
  • 处理请求参数:通过 request.getParameter() 获取表单或 URL 参数(自动解析 application/x-www-form-urlencoded 类型)。

3. 响应生成与返回

  • Servlet 处理完成后,将结果写入 ServletResponse(如 response.getWriter().println("Hello"))。
  • 响应通过 Connector 转换为字节流,按 HTTP 协议格式(状态行、响应头、响应体)发送给客户端。

4. 资源清理

  • 同步请求:关闭输入流(request.getInputStream().close())和输出流(response.getOutputStream().close())。
  • 异步请求:若使用 AsyncContext,则触发 ReadListener.onAllDataRead() 处理剩余数据,完成后调用 AsyncContext.complete()

协议升级处理

当客户端请求协议升级(如 HTTP/1.1 升级到 WebSocket)时,处理流程如下:

  1. ConnectionHandler 从当前 Processor 中获取 UpgradeToken(包含新协议处理器)。
  2. 创建升级后的 Processor 实例,替换当前 Processor。
  3. 调用 HttpUpgradeHandler.init() 初始化新协议处理器,开始使用新协议通信(如 WebSocket 的帧处理)。

欢迎关注我的其它发布渠道