概叙

科普文:SpringBoot项目禁用Tomcat,并用Undertow替换_springboot tomcat 禁用jsp-CSDN博客

科普文:Java web应用性能分析之【Spring Boot内嵌服务器选型:Tomcat vs Jetty vs Undertow 】-CSDN博客

在 Java web应用领域,SpringBoot 以其“开箱即用”的特性成为开发者的首选。

SpringBoot 作为当前最流行的 Java Web 开发框架,极大地降低了开发者的配置复杂度,使得开发人员可以迅速搭建一个完整的 Web 服务。在 Web 项目中,容器是至关重要的组件,因为它负责处理 HTTP 请求,并提供运行环境。

Tomcat 作为 Spring Boot 默认的嵌入式服务器,支持 Servlet 和 JSP,具有完整的 Web 服务器功能,并且包含 Tomcat 管理平台、安全控制和过滤机制。然而,随着高并发业务需求的增长,Tomcat 在性能和资源消耗方面暴露出一定的局限性,如今很多应用选择 Undertow 作为更优的替代方案。

Undertow 是由 Red Hat 开发的高性能 Web 服务器,完全采用 Java 编写,支持阻塞和非阻塞 IO,并且能够无缝嵌入到 Java 应用程序中。此外,Undertow 还原生支持 Servlet 和 WebSocket,使其在高并发场景下表现卓越。

为何替代Tomcat选择 Undertow ?

对于普通 Web 应用,Tomcat 依然是一个不错的选择,但如果你的系统需要应对高并发请求,或者希望在微服务架构下提升整体性能,那么 Undertow 无疑是一个更优的选择。

随着业务需求的增长和流量的激增,Tomcat 在高并发场景下的性能瓶颈逐渐显现,主要体现在以下几个方面:

  1. 线程模型的局限性 
    Tomcat 采用基于线程池的阻塞 I/O 模型,每个请求都会分配一个独立的线程来处理。这种方式在低并发场景下表现良好,但当请求量激增时,大量线程的创建和切换会导致 CPU 资源被大量消耗,线程上下文切换开销大,容易引发性能下降甚至 OOM(内存溢出)问题。
  2. 高并发下吞吐量有限 
    在压力测试中,Tomcat 的 QPS(每秒查询数)在并发数提升后增速明显放缓,甚至会因线程池饱和而导致请求阻塞或超时。由于 Tomcat 依赖于同步阻塞的 Servlet API,在高并发环境下无法充分利用现代 CPU 多核架构的能力。
  3. 响应时间较长 
    由于 Tomcat 采用同步阻塞 I/O,在请求处理时,如果某个请求需要访问数据库或外部 API,线程会一直处于等待状态,直到请求完成后才释放。这导致 Tomcat 在高并发时容易出现响应延迟,影响用户体验。
  4. 资源消耗较大 
    Tomcat 需要维护大量的线程,每个线程都会消耗一定的内存,导致整体系统资源占用较高。在 JVM 堆内存较小的情况下,大量的线程开销会进一步降低系统稳定性。
  5. 连接管理能力不足 
    Tomcat 默认使用短连接(HTTP 1.1 的 Keep-Alive 需要手动优化),在高并发环境下,短连接的频繁创建和关闭会导致额外的 TCP 握手和挥手开销,从而影响系统吞吐量。而 Undertow 采用持久连接,可以更高效地管理长连接,提高并发能力。

Tomcat vs Undertow 对比分析

性能对比

Tomcat 是 Apache 基金会旗下的轻量级 Servlet 容器,适用于一般 Web 应用。然而,在高并发场景下,Tomcat 处理请求的吞吐量相对较低。相比之下,Undertow 采用更高效的 IO 处理模型,使其在大规模请求下表现更优。

并发吞吐能力

在相同硬件环境下,我们对 Tomcat 和 Undertow 进行了并发测试,得出的 QPS(每秒请求数)如下:

  • Tomcat并发请求数较大时,QPS 明显下降,吞吐量受限。
  • Undertow默认使用持久连接,在高并发下吞吐量表现更优。

内存占用

内存管理方面,Tomcat 由于线程模型相对传统,在大量请求下容易导致内存消耗较高。而 Undertow 采用异步非阻塞架构,能够更高效地利用系统资源,减少内存开销。

Spring Boot源码分析

Spring Boot 2.6 通过 EmbeddedServletContainerAutoConfiguration(实际为 ServletWebServerFactoryAutoConfiguration)实现嵌入式容器的自动化配置,结合条件注解和 spring.factories 机制动态适配上下文环境‌。

启动时根据依赖和配置参数选择容器类型,最终通过 SpringApplication.run() 的刷新流程完成上下文初始化与服务暴露‌。

一、Spring Boot上下文环境初始化流程

  1. SpringApplication 对象构造阶段

    • 通过 SpringApplication.run() 方法启动时,首先根据 WebApplicationType.deduceFromClasspath() 判断应用类型(Servlet、Reactive 或 None),决定是否创建 Web 上下文环境‌。
    • 加载 META-INF/spring.factories 中的自动配置类,包括 EmbeddedServletContainerAutoConfiguration(实际对应 ServletWebServerFactoryAutoConfiguration)‌。
  2. 自动配置触发

    • 在 run() 方法执行过程中,SpringApplication 调用 refreshContext() 刷新上下文,触发 EmbeddedServletContainerAutoConfiguration 的加载‌。
    • 该配置类通过条件注解 @ConditionalOnClass 和 @ConditionalOnWebApplication 确保仅在 Servlet Web 环境下生效,并自动装配 ServletWebServerFactory 实例(如 Tomcat、Undertow 等)‌。

二、EmbeddedServletContainerAutoConfiguration 的核心作用

  1. 嵌入式容器工厂选择

    • 根据类路径中存在的依赖库(如 tomcat-embedded-core 或 undertow-core),自动选择对应的 ServletWebServerFactory 实现类‌。
    • 例如,若存在 Tomcat 依赖,则创建 TomcatServletWebServerFactory 实例,初始化内嵌 Tomcat 容器‌。
  2. 端口与参数配置

    • 通过 ServerProperties 类读取 server.portserver.servlet.context-path 等配置参数,动态设置容器的监听端口和上下文路径‌。
    • 若未显式配置端口,则使用默认值(如 Tomcat 默认 8080)‌。

ServletWebServerFactoryAutoConfiguration 源码解析

Spring Boot 2.6 中 ServletWebServerFactoryAutoConfiguration 源码解析:

ServletWebServerFactoryAutoConfiguration 是 Spring Boot 2.6 中嵌入式 Web 容器的核心自动配置类,通过条件注解动态选择容器类型,并绑定 server.* 参数实现灵活配置‌。

其设计体现了 Spring Boot “约定优于配置”的核心思想,同时兼顾扩展性和性能优化需求‌。


2.1 核心作用与配置触发机制

ServletWebServerFactoryAutoConfiguration 是 Spring Boot 嵌入式 Web 容器自动配置的核心类,通过条件注解实现容器工厂的动态选择‌。其核心流程包括:

  1. 条件化触发‌:
    • 通过 @ConditionalOnWebApplication 确保仅在 Servlet Web 环境下生效‌。
    • @ConditionalOnClass 检测类路径中是否存在 Servlet 和 ServletWebServerFactory 类,确认当前环境支持 Servlet 容器‌。
  2. 容器工厂选择‌:根据依赖库(如 tomcat-embedded-coreundertow-core)动态创建对应的 ServletWebServerFactory 实现类(如 TomcatServletWebServerFactoryUndertowServletWebServerFactory)‌。
2.2 容器工厂的创建与属性绑定
  1. 工厂 Bean 定义‌:

    • 通过内部静态类 EmbeddedTomcatEmbeddedJettyEmbeddedUndertow 定义条件化工厂 Bean‌。例如:
      @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })  
      @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)  
      static class EmbeddedTomcat {  
          @Bean  
          TomcatServletWebServerFactory tomcatServletWebServerFactory(...) { ... }  
      }  
      
    • @ConditionalOnMissingBean 确保用户未自定义 ServletWebServerFactory 实现时才会生效‌。
  2. 属性配置绑定‌:通过 ServerProperties 类绑定 server.* 配置(如端口、上下文路径),并在工厂初始化时注入参数‌。例如:

    @Bean  
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(  
        ServerProperties serverProperties) {  
        return new ServletWebServerFactoryCustomizer(serverProperties);  
    }  
    
2.3 自动配置的触发时机与流程
  1. 启动阶段触发‌:

    • 在 SpringApplication.run() 的 refreshContext() 阶段,通过 @EnableAutoConfiguration 触发自动配置类加载‌。
    • META-INF/spring.factories 中声明了 ServletWebServerFactoryAutoConfiguration,由 AutoConfigurationImportSelector 筛选并加载‌。
  2. 容器初始化流程‌:

    • 在 onRefresh() 生命周期中调用 createWebServer(),利用已创建的 ServletWebServerFactory 实例启动嵌入式容器(如 Tomcat)‌。
    • 最终通过 finishRefresh() 完成容器启动,对外提供服务‌。
2.4 关键设计思想
  1. 条件化配置‌:通过组合 @ConditionalOnClass@ConditionalOnMissingBean 等注解,实现“按需加载”,避免冗余配置‌。
  2. 扩展性与灵活性‌:
    • 用户可通过排除默认依赖并添加其他容器库(如 Undertow)无缝切换 Web 服务器‌。
    • 支持自定义 ServletWebServerFactory 实现,覆盖默认行为‌。

三、配置加载与上下文环境适配

  1. 条件化配置加载

    • @EnableAutoConfiguration 注解触发自动配置机制,加载 spring-boot-autoconfigure 模块中的预定义配置类‌。
    • 通过 @Import 导入 AutoConfigurationImportSelector,筛选并激活符合条件的配置类(如 ServletWebServerFactoryAutoConfiguration)‌。
  2. 容器上下文环境准备

    • 在 onRefresh() 生命周期阶段,调用 createWebServer() 方法创建嵌入式容器实例,并绑定到 Spring 上下文中‌。
    • 最终通过 finishRefresh() 完成容器启动,对外提供 HTTP 服务‌。

替代Tomcat选择 Undertow的效果

    Logo

    助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

    更多推荐