在 Spring Boot 应用中,嵌入式 Servlet 容器承担着接收 HTTP 请求、管理连接、驱动 Servlet 生命周期的职责。Tomcat 是默认选择,但 Jetty 和 Undertow 在特定场景下往往能带来更低的资源占用或更高的并发吞吐。理解三者的线程模型与配置差异,比单纯更换依赖更重要,因为调优的本质是让容器行为和业务负载相匹配。

运行模型差异:线程策略决定吞吐上限
Tomcat 从 8.x 开始默认使用 NIO 连接器,一个连接并不会永久独占工作线程,但在执行 Servlet 的读写操作时,工作线程仍然会阻塞到业务处理完成。这种模型对传统的请求-响应式应用很友好,开发人员也习惯在线程局部变量中保存上下文。然而当并发连接数很高、请求处理时间较长或存在大量长连接时,线程池的调度开销会逐渐成为瓶颈。
Jetty 同样基于 NIO,不过它的连接处理和线程池设计更加轻量。Jetty 内部大量使用异步回调,对空闲连接的清理和超时控制更精细,适合需要维持大量 WebSocket 长连接的场景。Undertow 则走得更彻底,底层使用 XNIO,将 IO 线程与工作线程明确分离。IO 线程只负责网络事件读写,真正的业务逻辑由独立的工作线程池执行,这种设计能够用较少的线程支撑较高的并发,尤其适合低延迟、非阻塞风格的服务。
三种容器的差异并不是单纯的优劣对比,而是取舍不同。Tomcat 胜在稳定和生态兼容性,Jetty 偏向轻量和嵌入灵活,Undertow 追求高并发下的低资源消耗。选型前要明确应用是计算密集、IO 密集还是长连接密集,因为不同类型的负载对线程模型的敏感度完全不同。
选型维度:并发、内存、协议与生态兼容性
如果项目追求的是稳定性和最广泛的兼容性,Tomcat 仍是优先级最高的选择。大部分第三方监控、部署工具和运维平台都对 Tomcat 有成熟支持,遇到问题时解决方案也更容易找到。若应用需要嵌入到其他系统中,或者内存预算非常有限,Jetty 的轻量体积和模块化设计会带来明显优势。
对于高并发、大量短连接或需要与 Netty 等非阻塞框架协同工作的服务,Undertow 的表现通常更突出。它在基准测试中往往能以较少的内存占用获得更高的每秒处理能力,尤其是在接口逻辑较轻、IO 等待比例较高的场景下。协议支持方面,三者都已支持 HTTP/2 和 WebSocket,但开启方式和配置细节略有不同,建议结合自己的基础设施统一规划。
切换容器时,可以通过 Maven 配置排除默认依赖。以下是一个使用 Undertow 替换 Tomcat 的示例:
<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>
这段配置会从 Web 启动器中移除 Tomcat,再加入 Undertow 的启动器。如果选择 Jetty,只需将第二个依赖替换为 spring-boot-starter-jetty。需要注意的是,某些基于 Tomcat 特定 API 的代码或过滤器在切换后可能会失效,因此切换前应做完整的回归测试。
通用调优参数:线程池、连接队列与超时
嵌入式容器的调优大多集中在线程池和连接管理上。以 Tomcat 为例,server.tomcat.threads.max 控制最大工作线程数,min-spare 控制最小空闲线程数。线程数并非越大越好:线程过多会导致频繁的上下文切换,且每个线程都会占用一定的栈内存。一般建议先通过压测找到当前 CPU 核数下的最佳并发区间,再逐步调整。
连接队列和超时参数同样关键。accept-count 表示当所有工作线程都在忙碌时,操作系统还能接受的等待连接数;超过这个数量的连接会被拒绝。这个值过小会导致突发流量下大量请求直接失败,过大会增加请求排队延迟。合理的做法是结合客户端重试机制,将等待队列控制在一个可接受的范围内,让系统快速失败而不是长时间无响应。
下面的配置展示了一组适合中等规模 Web 服务的 Tomcat 参数:
server.tomcat.threads.max=200 server.tomcat.threads.min-spare=20 server.tomcat.accept-count=100 server.tomcat.max-connections=10000 server.tomcat.connection-timeout=5000 server.tomcat.keep-alive-timeout=10000
Java 配置类也可以实现同样的效果,便于在代码中根据环境动态调整。例如通过实现 WebServerFactoryCustomizer 接口来统一管理容器参数:
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TomcatTuningConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (factory instanceof TomcatServletWebServerFactory) {
TomcatServletWebServerFactory tomcat = (TomcatServletWebServerFactory) factory;
tomcat.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
protocol.setMaxThreads(200);
protocol.setMinSpareThreads(20);
protocol.setAcceptCount(100);
protocol.setMaxConnections(10000);
protocol.setConnectionTimeout(5000);
protocol.setKeepAliveTimeout(10000);
});
}
}
}
这段代码针对 Tomcat 的 NIO 协议处理器进行定制。如果切换到 Jetty 或 Undertow,配置方式不同,但调整的核心思想是一致的:工作线程数量适中、等待队列有边界、超时时间与业务响应时间匹配。
容器特定调优与常见避坑点
不同容器还提供了一些专属参数。Undertow 通过 server.undertow.threads.io 设置 IO 线程数,通常建议与 CPU 核数相当;server.undertow.threads.worker 设置工作线程数,需要根据业务阻塞程度调整。Jetty 的线程池可以通过 server.jetty.threads.max 和 server.jetty.threads.min 控制,此外还可以设置 server.jetty.max-http-form-post-size 来限制表单提交大小,避免内存被意外撑爆。
Tomcat 方面,除了线程参数,还有 max-keep-alive-requests 控制 keep-alive 连接上最多处理的请求数,超过后主动关闭连接,有助于防止连接长时间占用资源。如果不需要 AJP 协议,务必确保 AJP 连接器未被启用,历史上 AJP 相关的安全漏洞不少,禁用它可以减少攻击面。
调优过程中有一些常见误区。比如以为把所有超时时间调大就能减少连接断开,实际上是让半死不活的连接占着资源;还有人在切换到 Undertow 后仍然沿用 Tomcat 的线程数配置,导致工作线程过多而 IO 线程不足。最稳妥的方式是每次只调整一个关键参数,用压测数据验证收益,再决定是否继续叠加变化。
另一个容易忽略的点是异步请求的支持。如果业务中使用了 DeferredResult 或 Servlet 3.1 的异步特性,要确保容器的异步超时和线程池配置与异步执行器协调一致,否则可能出现请求线程被快速耗尽或异步任务被提前中断的情况。
嵌入式Servlet容器Tomcat调优Jetty调优修改时间:2026-10-06 20:00:17