2026年7月11日 · 5 分钟阅读

高性能网络应用实践:手写 Tomcat、Disruptor 与百万连接思路

梳理手写 Tomcat 容器、Servlet 规范抽象、Netty 请求处理、Disruptor RingBuffer、生产者消费者模型,以及 Netty 与 Disruptor 结合的高性能网络应用思路。

网络编程学到 Netty 之后,可以继续往两个方向走:一个是理解 Web 容器如何处理 HTTP 请求,另一个是理解高并发网络应用如何削峰、解耦和提高吞吐。

手写 Tomcat 和使用 Disruptor 提升 Netty 应用性能,正好对应这两个方向。

手写 Tomcat 要解决什么

Tomcat 本质上是一个 Web 容器。它要做的事情包括:

  1. 监听端口。
  2. 接收客户端 HTTP 请求。
  3. 解析请求行、请求头、请求体。
  4. 根据 URL 找到对应 Servlet。
  5. 调用 Servlet 处理业务。
  6. 生成 HTTP 响应。
  7. 把响应写回客户端。

手写一个简化版 Tomcat,不是为了替代 Tomcat,而是为了理解 Web 容器的核心工作流。

Servlet 规范抽象

一个最小的 Servlet 容器通常需要几个抽象:

抽象作用
Request封装请求信息
Response封装响应写出能力
Servlet定义业务处理入口
Server监听端口并接收连接
Handler处理连接中的请求和响应

Servlet 可以设计成:

public interface HeroServlet {
    void service(HeroRequest request, HeroResponse response);
}

业务方只需要实现 Servlet,不需要关心底层连接如何建立、HTTP 报文如何写回。

请求和响应封装

Request 通常负责解析:

  • 请求方法。
  • 请求路径。
  • 查询参数。
  • 请求头。
  • 请求体。

Response 通常负责:

  • 设置状态码。
  • 设置响应头。
  • 写出响应体。
  • 按 HTTP 协议格式输出。

把请求和响应封装起来后,业务代码就不用直接操作 Socket 或 ByteBuf。

基于 Netty 的容器处理流程

如果用 Netty 实现简化 Web 容器,流程可以是:

BossGroup 接收连接
WorkerGroup 处理读写
HTTP 编解码器解析请求
自定义 Handler 转换 Request/Response
根据 URL 分发到 Servlet
Servlet 执行业务
Handler 写回响应

Netty 负责高性能网络通信,容器代码负责协议和业务分发。

为什么需要 Disruptor

当连接数和请求量继续上升,单靠 Handler 里直接执行业务可能不够。

常见问题包括:

  • IO 线程被业务逻辑阻塞。
  • 请求突增时缺少缓冲。
  • 多线程队列竞争严重。
  • 生产者和消费者之间吞吐不足。

Disruptor 是一个高性能并发框架,核心思想是使用 RingBuffer 作为事件队列,降低锁竞争和内存抖动。

RingBuffer

RingBuffer 是环形缓冲区。

可以把它理解为一个固定大小的数组,生产者把事件写入指定位置,消费者按顺序读取。

[0] [1] [2] [3] [4] [5] [6] [7]
 ^                           ^
 consumer                    producer

相比普通阻塞队列,RingBuffer 更关注:

  • 预分配内存。
  • 减少对象创建。
  • 降低锁竞争。
  • 提高缓存命中。

Disruptor 的核心角色

角色作用
Event事件对象,承载业务数据
EventFactory创建 Event
EventHandler消费 Event
RingBuffer存储 Event 的环形队列
Producer发布事件
Sequence表示生产和消费进度
WaitStrategy控制消费者等待策略

一个典型流程:

  1. 创建 EventFactory。
  2. 创建 EventHandler。
  3. 创建 Disruptor。
  4. 启动 Disruptor。
  5. 生产者向 RingBuffer 写入数据。
  6. 发布事件。
  7. 消费者处理事件。

单生产者单消费者

最简单场景是一个生产者、一个消费者。

生产者负责把业务数据放入 RingBuffer,消费者监听事件并处理。

这种模型适合先理解 Disruptor 的基本流程:

  • 申请序号。
  • 获取 Event 槽位。
  • 填充数据。
  • 发布事件。
  • 消费者处理。

多生产者多消费者

真实网络应用里,可能有多个 IO 线程同时投递事件,也可能有多个消费者并行处理业务。

这时要关注:

  • RingBuffer 大小。
  • 生产者类型。
  • 消费者依赖关系。
  • 等待策略。
  • 业务处理是否有序。

Disruptor 强大,但也更要求开发者理解事件流。

Netty + Disruptor

Netty 和 Disruptor 可以这样配合:

Netty IO 线程读取请求
  -> 封装业务事件
  -> 投递到 Disruptor RingBuffer
  -> 业务消费者处理
  -> 将结果写回 Netty Channel

这样做的价值是把 IO 和业务处理解耦。

Netty IO 线程尽量只做网络读写和轻量协议处理,不被耗时业务阻塞;业务逻辑交给 Disruptor 后面的消费者线程处理。

百万连接思路

百万连接不是一个单点参数,而是一组系统能力。

需要同时考虑:

  • 操作系统文件描述符限制。
  • TCP 参数。
  • Netty EventLoop 数量。
  • 堆内和堆外内存。
  • 连接空闲检测。
  • 心跳机制。
  • 消息编解码效率。
  • 业务线程池隔离。
  • 单机和集群容量规划。

高连接数不等于高吞吐。大量空闲长连接和大量活跃请求,对系统压力完全不同。

工程上的关键原则

高性能网络应用通常遵循几个原则:

  1. IO 线程不要执行耗时业务。
  2. 编解码要减少对象创建和复制。
  3. 队列要有边界,避免无限堆积。
  4. 连接要有心跳和超时清理。
  5. 业务线程池要隔离。
  6. 所有优化都要通过压测验证。

没有监控和压测的百万连接,只是想象中的百万连接。

小结

手写 Tomcat 帮我们理解 Web 容器的核心:监听连接、解析请求、分发 Servlet、写回响应。

Disruptor 帮我们理解高性能事件处理:预分配 RingBuffer、降低锁竞争、解耦生产者和消费者。

Netty 解决网络通信,Disruptor 解决事件流转,二者结合可以支撑更复杂的高并发网络应用。但真正的性能来自完整链路设计,而不是某个框架名字。