Tomcat 线程模型与请求处理(Executor + Worker 线程池)

在这里插入图片描述

1. 引言

在前几篇文章中,我们已经解析了 Connector 接收请求Container 容器路由Servlet 执行流程
但要支撑 高并发请求处理,Tomcat 不能只依靠单线程:

  • 一个请求需要绑定到一个线程处理
  • 线程过多会导致系统崩溃(线程切换开销)
  • 必须引入 线程池(Executor) 来复用线程

因此,本篇我们深入剖析 Tomcat 的线程模型与请求处理机制


2. Tomcat 的线程模型演进

(1) 传统 BIO(阻塞 I/O)

  • 每个请求一个线程
  • 若请求等待时间长(如网络慢、IO阻塞),会大量占用线程
  • 适合小规模并发
Socket → 一个请求一个线程

(2) NIO(非阻塞 I/O)

  • 使用 Selector 统一管理 Socket
  • 请求就绪时分配线程处理
  • 大幅减少线程占用
Socket → Selector → 可读/可写 → 线程池处理

(3) NIO.2 (AIO)

  • 基于操作系统的异步 IO
  • 请求到达时直接触发回调
  • Tomcat 虽支持,但在高并发下实际效果不一定优于 NIO

3. Tomcat 中的 Executor(线程池)

Tomcat 使用 Executor(线程池) 管理 Worker 线程。

配置示例(server.xml):

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
          maxThreads="200" minSpareThreads="25"/>

Connector 引用 Executor:

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
           executor="tomcatThreadPool"/>

4. 请求处理流程

请求处理的完整链路:

1. Acceptor 线程:接收 Socket 连接
2. Poller 线程:监听 Socket 是否可读(NIO Selector)
3. Worker 线程:从线程池取出线程,执行请求处理

👉 流程图:

Socket → Acceptor → Poller → Executor(Worker) → Container → Servlet

5. 核心源码解析

(1) Acceptor 接收请求

protected class Acceptor extends AbstractEndpoint.Acceptor {
    @Override
    public void run() {
        while (running) {
            SocketChannel socket = serverSock.accept();
            if (socket != null) {
                // 交给 Poller 注册到 Selector
                poller.register(socket);
            }
        }
    }
}

(2) Poller 监听事件

protected class Poller implements Runnable {
    @Override
    public void run() {
        while (true) {
            int keyCount = selector.select();
            if (keyCount > 0) {
                Set<SelectionKey> keys = selector.selectedKeys();
                for (SelectionKey key : keys) {
                    // 交给 Worker 线程处理
                    processKey(key);
                }
            }
        }
    }
}

(3) Worker 线程执行

protected class Worker implements Runnable {
    SocketWrapperBase<?> socket;
    @Override
    public void run() {
        // 请求封装为 Request/Response
        Http11Processor processor = new Http11Processor();
        processor.process(socket);
    }
}

(4) Processor 处理请求

public class Http11Processor {
    public SocketState process(SocketWrapperBase<?> socket) {
        // 解析 HTTP 请求行、头部、Body
        prepareRequest(socket);
        // 交给容器处理
        adapter.service(request, response);
        return SocketState.CLOSED;
    }
}

6. 线程池调度策略

Tomcat 的 Executor 底层是 线程池实现(ThreadPoolExecutor),关键参数:

  • maxThreads:最大线程数(并发上限)
  • minSpareThreads:最小空闲线程数
  • maxIdleTime:线程空闲存活时间
  • queueSize:请求排队队列大小

运行时调度逻辑:

  1. 有空闲线程 → 直接分配
  2. 没有空闲线程 → 放入队列
  3. 队列满了 → 若没到 maxThreads,则创建新线程
  4. 超过 maxThreads → 拒绝请求

7. 配置优化建议

(1) Executor 线程池

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
          maxThreads="500" minSpareThreads="50" maxIdleTime="60000"/>

(2) Connector 配置

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
           executor="tomcatThreadPool"
           acceptCount="100"
           connectionTimeout="20000"/>

参数解释:

  • acceptCount:等待队列大小,超过则拒绝连接
  • connectionTimeout:请求超时时间

8. 总结

在本篇文章中,我们完整解析了 Tomcat 的线程模型与请求处理

  • 三种模型:BIO(阻塞)、NIO(非阻塞)、AIO(异步 IO)
  • 请求链路:Acceptor → Poller → Executor(Worker)
  • Executor 线程池:高效复用线程,避免资源耗尽
  • 核心源码:Acceptor、Poller、Worker、Http11Processor
  • 配置优化:合理设置 maxThreadsacceptCountconnectionTimeout

👉 到这里,我们已经理解了 Tomcat 如何利用线程池支撑高并发请求处理


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐