从 Tomcat 与 Jetty 的对比,聊聊影响一个服务并发能力的关键因素

大家好,我是G探险者!

在 Java 服务体系中,TomcatJetty 是最常见的 Web 容器。很多人认为,只要加大连接数或线程数,就能提升并发能力。

但事实远比想象复杂:连接数只是冰山一角,背后还有线程模型、I/O 模型、内核资源限制等多重因素。

本文从 Tomcat 与 Jetty 的对比 入手,系统讲清楚------到底是什么在决定你的服务并发上限。


🧩 一、Tomcat 与 Jetty 的架构差异

1️⃣ Tomcat 的工作模型

Tomcat 默认采用 多线程 + 阻塞 I/O (BIO/NIO) 模型。

它通过 maxConnections 限制 TCP 连接数,通过 maxThreads 限制同时处理的请求线程。

简化模型如下:

css 复制代码
[客户端连接] → [TCP连接池 (maxConnections)] 
      ↓
[请求队列 (acceptCount)] 
      ↓
[线程池 (maxThreads)] → [业务处理]
  • 一个请求在被线程接管后,线程会一直阻塞直到请求完成。
  • 若请求阻塞(如访问数据库或远程服务),线程被占用,会直接影响整体并发。

2️⃣ Jetty 的工作模型

Jetty 默认使用 非阻塞 NIO 模型 ,基于 Reactor 模式。

它的核心理念是:

用少量 Selector 线程负责所有连接的读写事件,用工作线程处理真正的业务逻辑。

模型如下:

css 复制代码
[客户端连接] → [Selector线程 (少量)]
      ↓
[事件分发] → [工作线程池 (maxThreads)] → [业务处理]

优势:

  • 一个线程可同时管理上千连接(非阻塞 I/O);
  • 支持更高的空闲连接与 KeepAlive 连接数;
  • 在高并发长连接场景(如 WebSocket、HTTP/2)中明显优于 Tomcat。

⚙️ 二、Tomcat 与 Jetty 的默认并发参数对比

项目 Tomcat(Spring Boot 默认) Jetty(Spring Boot 可选) 说明
默认线程数 200 (server.tomcat.max-threads) 200 (QueuedThreadPool.maxThreads) 可同时处理请求的线程数
默认最大连接数 10000 (server.tomcat.max-connections) 无固定值(由 Selector 与 OS 限制) 同时打开的 TCP 连接数上限
默认等待队列 100 (server.tomcat.accept-count) 内部队列(动态) 连接满后等待请求数
I/O 模型 阻塞式 NIO 事件驱动式 NIO 决定每个线程能管理多少连接
KeepAlive 管理 每连接占线程资源 非阻塞复用线程 Jetty 更高效
配置灵活性 通过 server.tomcat.* 通过 server.jetty.* 或 Java API Jetty 灵活但复杂
典型并发能力 5K~10K(视CPU与内存) 10K~30K(视操作系统限制) Jetty 在高连接数场景下优势明显

🔍 三、影响服务并发能力的核心因素

很多人只调 maxThreadsmaxConnections,其实那只是表象。

一个服务的真正并发能力受以下五个层面的影响:


1️⃣ 应用层:线程模型与I/O模型

模型类型 代表容器 特点 并发能力
BIO(阻塞IO) Tomcat早期版本 每连接占线程
NIO(非阻塞IO) Tomcat 8+/Jetty 一个线程可处理多个连接 中高
AIO(异步IO) Jetty/Undertow/Netty 完全事件驱动

🧠 结论

选择非阻塞 I/O(如 Jetty、Netty)通常能显著提升并发上限。


2️⃣ 容器层:线程池与连接池配置

以 Tomcat 为例:

ini 复制代码
server.tomcat.max-connections=20000
server.tomcat.max-threads=500
server.tomcat.accept-count=1000
  • maxConnections:并发TCP连接上限
  • maxThreads:同时处理请求上限
  • acceptCount:连接满后的排队长度

如果线程数配置过低,CPU空闲但请求积压;

配置过高,又会导致频繁上下文切换,反而变慢。

🧠 经验值

  • CPU密集型应用:maxThreads ≈ 核数 × 2
  • I/O密集型应用:maxThreads ≈ 核数 × 5~10

3️⃣ 系统层:操作系统文件描述符与TCP栈

操作系统对连接的限制非常关键:

限制项 Linux参数 说明
最大文件描述符数 ulimit -n 每个连接占一个fd
TCP backlog 队列 /proc/sys/net/core/somaxconn 等待接入的连接队列
TIME_WAIT 超时 /proc/sys/net/ipv4/tcp_fin_timeout 连接关闭延迟释放
端口复用 /proc/sys/net/ipv4/tcp_tw_reuse 减少端口耗尽

⚠️ 若 ulimit -n 太小(如 1024),即使 Tomcat 配了 20000 连接也无法生效。


4️⃣ 数据库层:连接池与事务等待

应用层的高并发最终要落到数据库连接上。

若连接池上限(如 HikariCP 的 maximumPoolSize)太小,会出现:

"Tomcat 能接 2000 个请求,但只有 50 个能访问数据库,其他都在排队。"

🧠 调优思路:

  • DB连接数 ≈ 应用活跃线程数 × 访问数据库比例
  • 用缓存/异步队列减少数据库访问占比

5️⃣ 网络层:负载均衡与KeepAlive策略

  • KeepAlive 过短 → 频繁重建连接,耗时;
  • KeepAlive 过长 → 连接资源被空闲占用;
  • 负载均衡(如 Nginx) 的超时与连接复用策略,也会直接影响并发稳定性。

🧠 建议:

  • 对静态请求使用 KeepAlive;
  • 对动态或长轮询请求启用超时检测与连接回收;
  • 配合 Nginx 使用 keepalive_timeout 30smax_fails 等策略。

📈 四、Tomcat 与 Jetty 并发性能对比总结

维度 Tomcat Jetty
适用场景 标准Web应用(同步请求为主) 高并发、长连接、WebSocket
默认配置 守旧(maxThreads=200) 灵活、事件驱动
可维护性 Spring Boot默认支持最佳 可扩展性强(嵌入式更灵活)
性能瓶颈 线程阻塞、队列等待 网络带宽、Selector数量
调优重点 maxThreads / maxConnections 线程池与连接限制Bean
理论并发上限 1万左右 2万~3万以上(视系统资源)

🧠 简单说:

  • Tomcat 胜在稳定与生态;
  • Jetty 胜在轻量与高并发。

🧭 五、结语:并发的核心不止"连接数"

很多人问:"Tomcat 支持 10000 连接,我能处理 10000 并发吗?"

答案是:不一定。

因为:

  • 并发连接数 ≠ 并发请求数;
  • 并发请求数 ≠ 并发业务处理数;
  • 真正能撑起高并发的,是架构、线程模型、系统配置与资源调度的整体平衡

🧠 最佳实践:

  1. 调优容器参数(连接数、线程数、队列长度);
  2. 优化应用逻辑(异步化、减少阻塞);
  3. 提升系统层资源限制(ulimit -n、TCP参数);
  4. 引入缓存与异步解耦(Redis、MQ);
  5. 在高并发长连接场景下,考虑 Jetty 或 Netty 替代 Tomcat。

✅ 总结一句话

影响服务并发能力的核心,不只是连接数。

它取决于:

  • 容器的 I/O 模型(阻塞 / 非阻塞)
  • 线程池与连接池配置
  • 操作系统资源限制
  • 后端依赖(数据库 / MQ)性能

而 Tomcat 与 Jetty 的差异,恰好体现了"并发设计哲学"的两种思路:

  • Tomcat:稳定可靠的线程池模型
  • Jetty:事件驱动的高连接复用模型

相关推荐
你的人类朋友2 小时前
“签名”这个概念是非对称加密独有的吗?
前端·后端·安全
幼稚园的山代王3 小时前
go语言了解
开发语言·后端·golang
kkjt01303 小时前
{MySQL查询性能优化索引失效的八大场景与深度解决方案}
后端
ss2733 小时前
手写MyBatis第107弹:@MapperScan原理与SqlSessionTemplate线程安全机制
java·开发语言·后端·mybatis
橙子家4 小时前
log4net 简介以及简单示例(.net8)
后端
间彧4 小时前
Spring Boot分布式WebSocket实现指南:项目实战与代码详解
后端
间彧4 小时前
Spring Boot集成WebSocket项目实战详解
后端
该用户已不存在6 小时前
工具用得好,Python写得妙,9个效率工具你值得拥有
后端·python·编程语言
im_AMBER7 小时前
Web 开发 30
前端·笔记·后端·学习·web