Skip to content

Tomcat请求处理机制详解

概述

Tomcat作为一个遵循Servlet规范的Web容器,其核心职责就是接收HTTP请求并将其转发给对应的Servlet进行处理。理解Tomcat的请求处理机制,对于Web应用开发、性能调优和问题排查都有重要意义。

请求处理全流程

从客户端发起请求到获得响应,整个过程经历多个组件的协作处理。

请求处理流程图

mermaid
flowchart TB
    A([客户端]) -->|HTTP请求| B[Connector]
    B -->|解析请求| C[Engine]
    C -->|路由匹配| D[Host]
    D -->|应用匹配| E[Context]
    E -->|Servlet映射| F[Wrapper]
    F -->|调用| G[Servlet]
    G -->|业务处理| H{处理结果}
    H -->|生成响应| I[Response]
    I -->|返回| A
    
    style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px,rx:20
    style B fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style C fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style E fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style F fill:#fce4ec,stroke:#c2185b,stroke-width:2px,rx:10
    style G fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,rx:10
    style H fill:#e0f2f1,stroke:#00695c,stroke-width:2px,rx:10
    style I fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:10

处理流程详解

整个请求处理可以分为五个核心阶段:

第一阶段:请求接收

Connector组件通过监听指定端口接收来自客户端的网络连接。不同的Connector支持不同的协议:

mermaid
flowchart LR
    subgraph 客户端请求
        A([浏览器])
        B([移动APP])
        C([API调用])
    end
    
    subgraph Connector层
        D[HTTP Connector<br/>端口:8080]
        E[HTTPS Connector<br/>端口:8443]
        F[AJP Connector<br/>端口:8009]
    end
    
    A --> D
    B --> E
    C --> D
    
    style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style E fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style F fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10

第二阶段:请求解析

接收到原始的网络数据后,Tomcat需要对其进行解析,提取出有意义的请求信息:

解析内容示例用途
请求方法GET、POST、PUT确定操作类型
请求URI/api/orders/123路由匹配
协议版本HTTP/1.1协议处理
请求头Content-Type、Cookie元数据获取
请求体JSON数据、表单数据业务数据
java
// 模拟请求解析过程
public class HttpRequestParser {
    
    public ParsedRequest parse(InputStream inputStream) throws IOException {
        ParsedRequest request = new ParsedRequest();
        
        // 解析请求行
        String requestLine = readLine(inputStream);
        String[] parts = requestLine.split(" ");
        request.setMethod(parts[0]);      // GET
        request.setUri(parts[1]);         // /products/list
        request.setProtocol(parts[2]);    // HTTP/1.1
        
        // 解析请求头
        Map<String, String> headers = new HashMap<>();
        String headerLine;
        while (!(headerLine = readLine(inputStream)).isEmpty()) {
            int colonIndex = headerLine.indexOf(":");
            String name = headerLine.substring(0, colonIndex).trim();
            String value = headerLine.substring(colonIndex + 1).trim();
            headers.put(name.toLowerCase(), value);
        }
        request.setHeaders(headers);
        
        // 解析请求体(如果有)
        String contentLength = headers.get("content-length");
        if (contentLength != null) {
            int length = Integer.parseInt(contentLength);
            byte[] body = inputStream.readNBytes(length);
            request.setBody(body);
        }
        
        return request;
    }
}

第三阶段:Servlet查找

根据解析出的URI,Tomcat需要在容器层次中逐级匹配,找到对应的Servlet:

mermaid
flowchart TB
    A[请求URI: /shop/api/products] --> B{Engine匹配}
    B -->|默认Engine| C{Host匹配}
    C -->|shop.example.com| D{Context匹配}
    D -->|/shop| E{Servlet映射}
    E -->|/api/*| F[ApiServlet]
    
    style A fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:10
    style B fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style C fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style D fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style E fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style F fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10

Servlet映射规则优先级:

  1. 精确匹配/api/products/detail 精确匹配
  2. 路径匹配/api/* 匹配以/api/开头的请求
  3. 扩展名匹配*.do 匹配特定扩展名
  4. 默认匹配/ 默认Servlet,处理未匹配的请求

第四阶段:Servlet处理

找到对应的Servlet后,请求被交给Servlet的service方法处理:

java
// 订单处理Servlet示例
public class OrderProcessingServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
            throws ServletException, IOException {
        
        // 获取请求参数
        String orderId = request.getParameter("orderId");
        
        // 业务处理
        OrderService orderService = getOrderService();
        Order order = orderService.findById(orderId);
        
        // 设置响应
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.write(toJson(order));
    }
    
    @Override
    protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {
        
        // 读取请求体
        BufferedReader reader = request.getReader();
        StringBuilder body = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            body.append(line);
        }
        
        // 解析并处理订单
        Order newOrder = parseOrder(body.toString());
        OrderService orderService = getOrderService();
        orderService.create(newOrder);
        
        // 返回创建结果
        response.setStatus(HttpServletResponse.SC_CREATED);
        response.getWriter().write("{\"status\":\"success\"}");
    }
}

第五阶段:响应返回

Servlet处理完成后,响应通过相同的路径返回给客户端:

mermaid
sequenceDiagram
    participant S as Servlet
    participant W as Wrapper
    participant C as Connector
    participant CL as 客户端
    
    S->>S: 设置响应状态码
    S->>S: 设置响应头
    S->>S: 写入响应体
    S->>W: 处理完成
    W->>C: 封装HTTP响应
    C->>C: 序列化响应数据
    C->>CL: 发送响应
    CL->>CL: 渲染/处理响应

Servlet生命周期

Servlet作为请求处理的核心组件,有着明确的生命周期管理机制。

生命周期状态图

mermaid
stateDiagram-v2
    [*] --> 加载: 容器启动/首次请求
    加载 --> 实例化: Class.newInstance()
    实例化 --> 初始化: init()
    初始化 --> 就绪: 初始化完成
    就绪 --> 服务中: service()
    服务中 --> 就绪: 处理完成
    就绪 --> 销毁中: 容器关闭
    销毁中 --> 已销毁: destroy()
    已销毁 --> [*]

生命周期三阶段

初始化阶段(init)

当Servlet首次被请求或容器启动时(配置了load-on-startup),容器会创建Servlet实例并调用init方法:

java
public class DatabaseConnectionServlet extends HttpServlet {
    
    private DataSource dataSource;
    private ExecutorService executorService;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        
        // 读取配置参数
        String poolSize = config.getInitParameter("poolSize");
        String dbUrl = config.getInitParameter("dbUrl");
        
        // 初始化数据源
        this.dataSource = createDataSource(dbUrl, Integer.parseInt(poolSize));
        
        // 初始化线程池
        this.executorService = Executors.newFixedThreadPool(10);
        
        log("DatabaseConnectionServlet initialized successfully");
    }
    
    private DataSource createDataSource(String url, int poolSize) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(url);
        config.setMaximumPoolSize(poolSize);
        return new HikariDataSource(config);
    }
}

init方法特点:

  • 只在Servlet生命周期中调用一次
  • 适合执行耗时的初始化操作(数据库连接、配置加载等)
  • 初始化失败会抛出ServletException,Servlet不可用

服务阶段(service)

每次请求到达时,容器都会调用service方法。HttpServlet的service方法会根据请求方法分发到对应的doXxx方法:

mermaid
flowchart TB
    A[service方法] --> B{请求方法?}
    B -->|GET| C[doGet]
    B -->|POST| D[doPost]
    B -->|PUT| E[doPut]
    B -->|DELETE| F[doDelete]
    B -->|HEAD| G[doHead]
    B -->|OPTIONS| H[doOptions]
    
    style A fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:10
    style B fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style C fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style E fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style F fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style G fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style H fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
java
// HttpServlet中的service方法实现
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    
    String method = req.getMethod();
    
    if (method.equals(METHOD_GET)) {
        // GET请求处理
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            // 处理条件GET
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else {
        // 不支持的方法
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
    }
}

service方法特点:

  • 每次请求都会调用
  • 在多线程环境下执行,需注意线程安全
  • 通过Request获取输入,通过Response输出结果

销毁阶段(destroy)

当容器关闭或Web应用被卸载时,容器会调用destroy方法:

java
public class DatabaseConnectionServlet extends HttpServlet {
    
    private DataSource dataSource;
    private ExecutorService executorService;
    
    @Override
    public void destroy() {
        // 关闭线程池
        if (executorService != null) {
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                executorService.shutdownNow();
            }
        }
        
        // 关闭数据源
        if (dataSource instanceof HikariDataSource) {
            ((HikariDataSource) dataSource).close();
        }
        
        log("DatabaseConnectionServlet destroyed, resources released");
    }
}

destroy方法特点:

  • 只调用一次,用于资源清理
  • 应该释放所有占用的资源(连接池、文件句柄等)
  • 调用后Servlet实例将被GC回收

生命周期完整时序

mermaid
sequenceDiagram
    participant C as 容器
    participant S as Servlet
    participant R as 请求1
    participant R2 as 请求2
    participant R3 as 请求N
    
    rect rgb(232, 245, 233)
        Note over C,S: 初始化阶段(仅一次)
        C->>S: new Servlet()
        C->>S: init(ServletConfig)
        S->>S: 加载配置/初始化资源
    end
    
    rect rgb(227, 242, 253)
        Note over C,R3: 服务阶段(多次)
        R->>C: HTTP请求
        C->>S: service(request, response)
        S->>R: HTTP响应
        
        R2->>C: HTTP请求
        C->>S: service(request, response)
        S->>R2: HTTP响应
        
        R3->>C: HTTP请求
        C->>S: service(request, response)
        S->>R3: HTTP响应
    end
    
    rect rgb(255, 243, 224)
        Note over C,S: 销毁阶段(仅一次)
        C->>S: destroy()
        S->>S: 释放资源
    end

过滤器与请求处理链

在实际的请求处理中,请求通常需要经过一系列过滤器(Filter)的处理。

过滤器链执行流程

mermaid
flowchart LR
    A([请求]) --> B[编码过滤器]
    B --> C[认证过滤器]
    C --> D[日志过滤器]
    D --> E[Servlet]
    E --> F[响应]
    F --> D
    D --> C
    C --> B
    B --> G([客户端])
    
    style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px,rx:20
    style E fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style B fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style C fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style D fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style G fill:#e1f5fe,stroke:#01579b,stroke-width:2px,rx:20

过滤器实现示例

java
// 请求日志过滤器
public class RequestLoggingFilter implements Filter {
    
    private static final Logger logger = LoggerFactory.getLogger(RequestLoggingFilter.class);
    
    @Override
    public void init(FilterConfig filterConfig) {
        logger.info("RequestLoggingFilter initialized");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 记录请求开始
        long startTime = System.currentTimeMillis();
        String requestId = UUID.randomUUID().toString().substring(0, 8);
        
        logger.info("[{}] Request started: {} {}", 
                   requestId, httpRequest.getMethod(), httpRequest.getRequestURI());
        
        try {
            // 继续执行过滤器链
            chain.doFilter(request, response);
        } finally {
            // 记录请求结束
            long duration = System.currentTimeMillis() - startTime;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            
            logger.info("[{}] Request completed: status={}, duration={}ms",
                       requestId, httpResponse.getStatus(), duration);
        }
    }
    
    @Override
    public void destroy() {
        logger.info("RequestLoggingFilter destroyed");
    }
}

请求处理中的线程模型

Tomcat使用线程池来处理并发请求,理解线程模型对于性能调优至关重要。

线程池工作模式

mermaid
flowchart TB
    subgraph 客户端请求
        R1([请求1])
        R2([请求2])
        R3([请求3])
        R4([请求N])
    end
    
    subgraph Acceptor
        A[Acceptor线程]
    end
    
    subgraph 工作线程池
        W1[Worker-1]
        W2[Worker-2]
        W3[Worker-3]
        WN[Worker-N]
    end
    
    subgraph 任务队列
        Q[(等待队列)]
    end
    
    R1 --> A
    R2 --> A
    R3 --> A
    R4 --> A
    
    A --> Q
    Q --> W1
    Q --> W2
    Q --> W3
    Q --> WN
    
    style A fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,rx:10
    style Q fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,rx:10
    style W1 fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style W2 fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style W3 fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10
    style WN fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,rx:10

线程池配置参数

在server.xml中可以配置Connector的线程池参数:

xml
<Connector port="8080" protocol="HTTP/1.1"
           maxThreads="200"
           minSpareThreads="10"
           acceptCount="100"
           connectionTimeout="20000"
           redirectPort="8443" />
参数默认值说明
maxThreads200最大工作线程数
minSpareThreads10最小空闲线程数
acceptCount100等待队列最大长度
connectionTimeout20000连接超时时间(ms)

小结

本文详细介绍了Tomcat的请求处理机制:

  1. 请求处理五阶段:接收 → 解析 → Servlet查找 → 处理 → 响应返回
  2. Servlet生命周期:init(一次)→ service(多次)→ destroy(一次)
  3. 过滤器链:请求和响应都会经过配置的过滤器处理
  4. 线程模型:基于线程池的并发处理机制

掌握这些知识,有助于编写高效的Servlet程序和进行Tomcat性能调优。

更新: 2025-12-04 17:41:16
原文: https://www.yuque.com/u22210564/zoxfmt/doc-30-tomcat-02

Java 后端面试知识库