1. 概述
随着云原生应用和微服务架构的普及,对嵌入式 Servlet 容器的需求日益增长。Spring Boot 默认支持三种成熟稳定的容器:Tomcat、Jetty 和 Undertow,开发者可以轻松切换使用。
本文将通过启动时和负载下的性能指标,快速对比这三种容器的实际表现。我们不会堆砌理论,而是用数据说话,帮你避开选型时的常见“坑”。
2. 依赖配置
所有测试均基于 spring-boot-starter-web
,这是 Web 应用的基础依赖。我们使用 Spring Boot 3.1.5 版本,结构清晰,开箱即用。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.1. Tomcat
✅ 默认容器,无需额外配置。spring-boot-starter-web
已自动包含 spring-boot-starter-tomcat
,直接启动即可。
2.2. Jetty
要替换为 Jetty,需先排除 Tomcat 依赖,再引入 Jetty starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
2.3. Undertow
配置方式与 Jetty 一致,仅替换 starter 名称:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
⚠️ 注意:排除依赖时务必确认 artifactId
正确,否则会同时加载多个容器,引发端口冲突或线程资源浪费。
2.4. Actuator
使用 Spring Boot Actuator 暴露运行时指标,便于监控和压测。添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
启动后可通过 /actuator/metrics
等接口获取 JVM 和系统指标。
2.5. Apache Bench
使用 ab
(Apache Bench)进行简单粗暴的压测。Linux 用户安装方式:
$ apt-get install apache2-utils
Windows 用户可从 Apache 官方推荐的第三方发行版下载,ab.exe
位于 bin
目录下。
3. 启动性能指标
3.1. 指标采集
我们通过监听 ApplicationReadyEvent
事件,在应用启动完成后自动采集关键指标,避免手动调用接口或使用 JMX 工具。
核心代码如下:
@Component
public class StartupEventHandler {
private static final String[] METRICS = {
"jvm.memory.used",
"jvm.classes.loaded",
"jvm.threads.live"
};
private static final String METRIC_MSG_FORMAT = "Startup Metric >> {}={}";
private final Logger logger = LoggerFactory.getLogger(StartupEventHandler.class);
private final MeterRegistry meterRegistry;
public StartupEventHandler(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@EventListener
public void getAndLogStartupMetrics(ApplicationReadyEvent event) {
Arrays.asList(METRICS).forEach(this::processMetric);
}
private void processMetric(String metricName) {
Meter meter = meterRegistry.find(metricName).meter();
if (meter != null) {
Double value = meter.measure().iterator().next().getValue();
logger.info(METRIC_MSG_FORMAT, metricName, value.longValue());
}
}
}
✅ 优点:无需暴露敏感接口,指标自动记录到日志,适合 CI/CD 流程。
3.2. 指标选择
我们关注三个核心指标,反映容器启动后的资源占用情况:
jvm.memory.used
:JVM 已使用内存总量(MB)jvm.classes.loaded
:已加载类数量,反映初始化开销jvm.threads.live
:活跃线程数,体现容器线程模型差异
这些指标能快速判断容器“轻量级”程度。
4. 运行时性能指标
4.1. 压测方式
使用 Apache Bench 对 /actuator/metrics
接口施加压力,命令如下:
ab -n 10000 -c 10 http://localhost:8080/actuator/metrics
参数说明:
-n 10000
:总请求数-c 10
:并发数
⚠️ 注意:实际项目应压测业务接口,此处仅为统一测试基准。
4.2. 关键指标
ab
输出中我们重点关注:
- Requests per second:每秒处理请求数,越高越好
- **Time per request (mean)**:单请求平均耗时(ms),越低越好
这两个指标直接反映吞吐能力和响应延迟。
5. 测试结果
以下数据基于默认配置的空项目,仅供参考。实际业务中差异可能更显著。
指标 | Tomcat | Jetty | Undertow |
---|---|---|---|
jvm.memory.used (MB) | 168 | 155 | 164 |
jvm.classes.loaded | 9869 | 9784 | 9787 |
jvm.threads.live | 25 | 17 | 19 |
Requests per second | 1542 | 1627 | 1650 |
Average time per request (ms) | 6.483 | 6.148 | 6.059 |
结论速览:
- ✅ 内存占用:Jetty 最小,Undertow 次之,Tomcat 略高
- ✅ 启动线程数:Jetty 最少(17),资源更轻量
- ✅ 吞吐性能:Undertow > Jetty > Tomcat,Undertow 领先约 7%
- ✅ 延迟:Undertow 平均延迟最低,响应更快
⚠️ 提示:测试基于 Actuator 接口的 GET 请求,I/O 密集型场景下 Undertow 的非阻塞模型优势更明显。
6. 压测分析
- ** workload 的影响**:本次测试为简单 GET 请求,若涉及复杂业务逻辑或数据库操作,结果可能不同。
- 工具局限性:
ab
简单直接,但无法模拟复杂场景。如需更精确结果,建议使用 JMeter 或 Gatling。 - 生产环境匹配:务必用接近生产流量的测试用例,避免“纸上谈兵”。
7. 容器选型建议
不要仅凭几个数字做决定。实际选型应综合考虑:
- ✅ 团队熟悉度:Tomcat 文档丰富,排查问题成本低
- ✅ 功能需求:如需 WebSocket、长连接,Jetty 和 Undertow 支持更好
- ✅ 性能要求:高并发、低延迟场景优先考虑 Undertow
- ✅ 生态兼容性:某些中间件对 Tomcat 适配最完善
- ✅ 策略限制:公司技术规范可能强制使用特定容器
简单粗暴总结:
- 求稳选 Tomcat
- 要轻量选 Jetty
- 要性能选 Undertow
8. 总结
本文通过实际指标对比了 Spring Boot 内置的三大 Servlet 容器。在默认配置下:
- Undertow 启动内存适中,运行时性能最优
- Jetty 内存和线程占用最小,适合资源受限环境
- Tomcat 综合表现均衡,生态最成熟
最终选择应结合业务场景、团队能力和运维策略。没有最好的容器,只有最适合的方案。
所有示例代码已托管至 GitHub:https://github.com/yourname/spring-boot-container-benchmark