Spring(21) 在 Spring Boot 中使用 Undertow 作为嵌入式服务器

目录

    • [一、四种 Web 服务器](#一、四种 Web 服务器)
      • [1.1 Tomcat 服务器](#1.1 Tomcat 服务器)
      • [1.2 Jetty 服务器](#1.2 Jetty 服务器)
      • [1.3 Undertow 服务器](#1.3 Undertow 服务器)
      • [1.4 Netty(响应式场景)](#1.4 Netty(响应式场景))
    • [二、Undertow 介绍](#二、Undertow 介绍)
    • [三、SpringBoot 中使用 Undertow](#三、SpringBoot 中使用 Undertow)
    • 四、配置属性
      • [4.1 配置文件](#4.1 配置文件)
      • [4.2 编程式配置](#4.2 编程式配置)
    • 五、补充
      • [5.1 启动时的警告日志](#5.1 启动时的警告日志)

一、四种 Web 服务器

通过 org.springframework.boot.autoconfigure.web.ServerProperties 查看,可以看到这里包括了 TomcatJettyNettyUndertow 四种服务器的设置,默认启用 Tomcat。

1.1 Tomcat 服务器

Tomcat: 是 Spring Boot 默认采用的 Web 服务器,通过 spring-boot-starter-web 依赖自动引入。

优点:

  • 成熟稳定: Apache Tomcat 是使用最广泛的开源 Servlet 容器之一,有着悠久的历史和庞大的用户群体,因此其稳定性与兼容性相对较好。
  • 易用性: 对于初学者和开发者来说,Tomcat 的配置和部署流程相对简单直接,由丰富的文档资源和支持社区。
  • 功能丰富: 支持所有主流的 Servlet、JSP 规范,以及许多附加模块,如:连接池管理、安全认证等。

缺点:

  • 性能对比: 在极端高并发或低延迟要求下,相比于 JettyUndertow,Tomcat 的性能可能稍逊一筹,尤其是在内存占用和线程模型方面。
  • 灵活性: 相比 Undertow,Tomcat的架构设计可能不够灵活,例如在处理大量短链接时,资源回收效率可能会收到影响。

1.2 Jetty 服务器

Jetty: 一个快速、高性能且轻量级的 Servlet 容器和 HTTP 服务器。若要使用 Jetty 替换 Tomcat,只需在项目中排除 Tomcat 并引入 Jetty 的起步依赖 spring-boot-starter-jetty

优点:

  • 高性能: Jetty 优化了对多核处理器的支持,并且提供了出色的并发性能,在某些测试场景下,它的启动速度和吞吐量优于 Tomcat
  • 轻量化: Jetty 可以高度定制化,适合用于轻量级服务或者对启动速度有较高要求的应用场景。
  • 模块化设计: 提供灵活的组件化结构,可以根据应用需求加载最小化的运行时环境。

缺点:

  • 市场占有率: 虽然非常优秀,但在市场占有率上相对于 Tomcat 较小,社区活跃度和第三方插件支持略少一些。
  • 学习曲线: 对于初次接触的开发者,Jetty 的配置和高级特性可能需要一定时间去熟悉。

1.3 Undertow 服务器

Undertow: 由 Red Hat 提供的一个灵活、高性能的 Web 服务器,可通过添加 spring-boot-starter-undertow 依赖来启用 Undertow 作为应用的内嵌服务器。

优点:

  • 极致性能: Undertow 设计理念追求极致性能,采用现代异步非阻塞 I/O 模型,尤其在高并发场景下表现卓越,能有效降低系统延迟并提高资源利用率。
  • 轻量级和模块化: 具备优秀的扩展性和自定义能力,可以按需加载模块,从而减少不必要的开销。
  • 创新设计: 它通过 XNIO 提供了先进的网络通信框架,可以动态创建和销毁线程资源,更好地适应现代硬件架构。

缺点:

  • 普及度: 相较于 TomcatJetty 的知名度和使用率可能较低,这意味着相关的教程和实践经验可能不如前者丰富。
  • 成熟度: 尽管已经很成熟,但在某些情况下,由于开发历史相对较短,特定问题的解决方案可能不如其他老牌服务器完善。

1.4 Netty(响应式场景)

Netty: 虽然 Netty 不是传统的基于 Servlet 的 Web 容器,但在 Spring Boot 中可以用于构建响应式的 Web 应用,尤其是通过 Reactive Stack(如 WebFlux)时,会使用 Netty 作为底层网络通信层。要使用 Netty,通常引入的是 spring-boot-starter-webflux 依赖。

优点:

  • 非阻塞式I/O: Netty 使用事件驱动和异步 I/O 模型,非常适合构建高性能、高并发的网络应用程序。
  • 反应式编程友好: 在 Spring WebFlux 中,Netty 作为底层传输层,可以无缝集成到响应式编程模型中,充分利用 Reactor 或 RxJava 的优势,实现真正的非阻塞服务端处理。
  • 低资源消耗: 在处理大量的并发连接时,尤其是短链接场景,Netty 能够高效地分配和释放资源,保持较小的内存占用。

缺点:

  • 复杂性: Netty API 相对复杂,需要开发者具有较高的网络编程知识才能充分发挥其中的性能优势。
  • Servlet 不适用: Netty 不直接支持传统的基于 Servlet 的 Web 应用。在 Spring WebFlux 中,需要采用 Reactive 编程模型来替代传统的 MVC 模式。

总的来说,大家可以根据以上优缺点来进行衡量,选择最适合项目的服务器。这里我们主要对 Undertow 服务器进行详细说明。


二、Undertow 介绍

Undertow 是一个轻量级的、高性能的 Java Web 服务器,由 JBoss 公司(Red Hat 旗下)开发并开源的。它是基于非阻塞(non-blocking)的 I/O 模型,具有低资源消耗和高并发处理能力。

Undertow 的优势如下:

  1. 支持 HTTP/2: Undertow 开箱即支持 HTTP/2,无需重写启动类路径。
  2. 支持 HTTP Upgrade: 允许通过 HTTP 端口复用多种协议。
  3. 支持 Web Socket: Undertow 提供对 Web Sockets 的全面支持,包括 JSR-356 支持。
  4. Servlet 4.0: Undertow 支持 Servlet 4.0,包括对嵌入式 Servlet 的支持。还可以在同一部署中混合使用 Servlet 和原生 Undertow 非阻塞 handler。
  5. 可嵌入式: 只需几行代码,即可将 Undertow 嵌入应用程序或独立运行。
  6. 灵活性: Undertow 通过链式 handler 进行配置,可以根据需求灵活地添加功能。

在很多场景的测试下,Undertow 的性能测试都高于 Tomcat。天生适合作为 Spring Boot 应用的嵌入式服务器。


三、SpringBoot 中使用 Undertow

如上所述,Spring Boot 默认使用 Tomcat 作为嵌入式服务。所以 spring-boot-starter-web 默认依赖了 spring-boot-starter-tomcat

要使用 Undertow 替换 Tomcat,首先要从 spring-boot-starter-web 中排除 Tomcat,再添加 spring-boot-starter-undertow 依赖:

xml 复制代码
<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>

仅此即可。启动应用,查看日志如下:

java 复制代码
2024-01-24 22:14:03.204  INFO 41412 --- [           main] io.undertow                              : starting server: Undertow - 2.0.33.Final
2024-01-24 22:14:03.211  INFO 41412 --- [           main] org.xnio                                 : XNIO version 3.3.8.Final
2024-01-24 22:14:03.221  INFO 41412 --- [           main] org.xnio.nio                             : XNIO NIO Implementation Version 3.3.8.Final
2024-01-24 22:14:04.029  INFO 41412 --- [           main] o.s.b.w.e.u.UndertowServletWebServer     : Undertow started on port(s) 8080 (http) with context path ''
2024-01-24 22:14:04.035  INFO 41412 --- [           main] com.demo.SpringbootDemoApplication       : Started SpringbootDemoApplication in 3.337 seconds (JVM running for 5.652)

如你所见,Spring Boot 已经使用 Undertow 作为嵌入式服务器。


四、配置属性

4.1 配置文件

Spring Boot 预制了很多属性,可用于在 application.properties | yaml 中对 Undertow 服务器进行个性化配置。

它们都以 server.undertow.* 开头,总结如下:

配置项 说明 示例
server.undertow.accesslog.dir Undertow 访问日志没目录。
server.undertow.accesslog.enabled 是否启用访问日志。 false
server.undertow.accesslog.pattern 访问日志的格式。 common
server.undertow.accesslog.prefix 日志文件前缀。 access_log
server.undertow.accesslog.rotate 是否开启日志滚动。 true
server.undertow.accesslog.suffix 日志文件后缀 log
server.undertow.always-set-keep-alive 是否应在所有响应中添加 Connection:keep-alive Header,即使 HTTP 规范没有要求。 true
server.undertow.buffer-size 每个 buffer 的大小。默认大小是根据 JVM 可用的最大内存确定的。
server.undertow.decode-slash 是否应解码已编码的斜线字符(%2F)。如果前端代理不执行相同的解码,解码可能会导致安全问题。只有在传统应用程序需要时才启用。设置后,server.undertow.allow-encoded-slash 无效。
server.undertow.decode-url 是否对 URL 进行编码。禁用时,URL 中的百分比编码字符将保持原样。 true
server.undertow.direct-buffers 是否在 Java 堆外分配 buffer。默认大小是根据 JVM 可用的最大内存确定的。
server.undertow.eager-filter-init 是否应在启动时初始化 servlet Filter true
server.undertow.max-cookies 允许的最大 cookie 数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。 200
server.undertow.max-headers 允许的最大 header 数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。
server.undertow.max-http-post-size HTTP post content 的最大大小。当值为 -1(默认值)时,大小为无限。 -1B
server.undertow.max-parameters 允许查询或路径参数的最大数量。这一限制是为了防止基于哈希碰撞的 DOS 攻击。
server.undertow.no-request-timeout 在服务器关闭连接之前,连接在不处理请求的情况下闲置的时间。
server.undertow.options.server.* io.undertow.UndertowOptions 中定义的服务器选项。
server.undertow.options.socket.* org.xnio.Options 中定义的 socket 选项。
server.undertow.preserve-path-on-forward 转发请求时是否保留请求路径。 false
server.undertow.threads.io I/O 线程数。默认值为可用的处理器数量。
server.undertow.threads.worker Worker 线程数。默认为 I/O 线程数的 8 倍。
server.undertow.url-charset 用于解码 URL 的字符集。 UTF-8

4.2 编程式配置

如果配置属性无法满足你的需求,你可以通过配置类,以编程式的方式进行定制。

实现 WebServerFactoryCustomizer<UndertowServletWebServerFactory 接口,重写 customize() 方法:

java 复制代码
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory>{

    @Override
    public void customize(UndertowServletWebServerFactory factory) {
        factory.setBufferSize(8192);
        // 其他自定义配置 ...
    }
}

对于 UndertowServletWebServerFactory 配置类的细节,请参阅:


五、补充

5.1 启动时的警告日志

当我们使用 Undertow 作为 Spring Boot 的嵌入式服务器时,启动应用后会看到有一条 WARN 日志,如下所示:

java 复制代码
WARN 9608 --- [           main] io.undertow.websockets.jsr               : UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used

大致意思是:"没有给 WebSocketDeploymentInfo 设置 Buffer pool,将会使用默认值"。

有两种方式可以解决这个问题:

方式一:排除 undertow-websockets-jsr 依赖

如果未使用到 WebSocket 技术,那么可以直接从 spring-boot-starter-undertow 中排除 undertow-websockets-jsr 依赖即可。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
    <exclusions>
        <!-- 排除 undertow-websockets-jsr 依赖 -->
        <exclusion>
            <groupId>io.undertow</groupId>
            <artifactId>undertow-websockets-jsr</artifactId>
        </exclusion>
    </exclusions>
</dependency>

方式二:为 WebSocketDeploymentInfo 设置合理的参数

也可以通过上述的 "编程式" 配置方式,为 WebSocketDeploymentInfo 设置一个合理的参数,如下所示:

java 复制代码
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;

import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;

@Configuration
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory>{

    @Override
    public void customize(UndertowServletWebServerFactory factory) {
        factory.addDeploymentInfoCustomizers(deploymentInfo -> {
            
            WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
            
            // 设置合理的参数
            webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 8192));
            
            deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
        });
    }
}

经测试,以上 2 种方式都可以解决 Undertow 启动时有警告日志的问题。

整理完毕,完结撒花~ 🌻

参考地址:

1.在 Spring Boot 中使用 Undertow 作为嵌入式服务器,https://springdoc.cn/spring-boot-undertow/

2.Springboot 优化内置服务器Tomcat优化(underTow),https://blog.csdn.net/qq_31536117/article/details/134499778

相关推荐
南山十一少4 小时前
Spring Security+JWT+Redis实现项目级前后端分离认证授权
java·spring·bootstrap
勤奋的凯尔森同学5 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
丁卯4046 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
chengooooooo6 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Tirzano6 小时前
springsecurity自定义认证
spring boot·spring
人间打气筒(Ada)7 小时前
MySQL主从架构
服务器·数据库·mysql
落笔画忧愁e8 小时前
FastGPT快速将消息发送至飞书
服务器·数据库·飞书
小冷爱学习!8 小时前
华为动态路由-OSPF-完全末梢区域
服务器·网络·华为
落幕9 小时前
C语言-进程
linux·运维·服务器
bing_1589 小时前
简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用
spring boot·后端·简单工厂模式