Tomcat Web 请求处理流程详解
Tomcat 作为 Servlet 容器,其核心功能是接收并处理客户端的 HTTP 请求。从请求进入服务器到响应返回客户端,涉及多个组件的协同工作。本文将详细解析 Tomcat 处理 Web 请求的完整流程,重点关注核心组件的交互逻辑。
请求处理总体流程
Tomcat 处理请求的入口是 org.apache.catalina.connector.CoyoteAdapter.service() 方法,该方法串联起从协议解析到 Servlet 执行的全流程。整体可分为以下步骤:
- 请求接收与协议适配
客户端请求通过 Connector 进入 Tomcat,Connector 根据协议类型(如 HTTP/1.1、AJP)创建对应的org.apache.coyote.Request(Coyote 请求对象)和org.apache.coyote.Response(Coyote 响应对象),并转换为 Tomcat 内部的org.apache.catalina.connector.Request和org.apache.catalina.connector.Response。 - 容器层级匹配
- Engine:根据请求的主机名(如
localhost)匹配对应的虚拟主机(Host)。 - Host:根据请求路径(如
/myapp)匹配对应的 Web 应用(Context)。 - Context:通过 URL 映射找到对应的 Servlet(由 StandardWrapper 管理)。
- Engine:根据请求的主机名(如
- 请求处理与响应生成
- 执行认证、授权等预处理。
- 调用目标 Servlet 的
service()方法处理请求。 - 将处理结果通过 Response 对象封装,反向传递给客户端。
- 资源清理
- 同步请求:关闭输入流和输出流。
- 异步请求:触发
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 | public void run() { |
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 版本。
- 解析请求头(如
Host、Content-Type)。 - 处理请求体(如表单数据、文件上传),并封装为参数。
解析完成后,CoyoteAdapter 将 CoyoteRequest 转换为 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)时,处理流程如下:
ConnectionHandler从当前 Processor 中获取UpgradeToken(包含新协议处理器)。- 创建升级后的 Processor 实例,替换当前 Processor。
- 调用
HttpUpgradeHandler.init()初始化新协议处理器,开始使用新协议通信(如 WebSocket 的帧处理)。