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

相关推荐
栗豆包7 分钟前
w118共享汽车管理系统
java·spring boot·后端·spring·tomcat·maven
BUG 40416 分钟前
LINUX--shell
linux·运维·服务器
万亿少女的梦16820 分钟前
基于Spring Boot的网络购物商城的设计与实现
java·spring boot·后端
菜鸟小白:长岛icetea22 分钟前
Linux零基础速成篇一(理论+实操)
linux·运维·服务器
过过过呀Glik1 小时前
在 Ubuntu 服务器上添加和删除用户
linux·服务器·ubuntu
开心工作室_kaic2 小时前
springboot485基于springboot的宠物健康顾问系统(论文+源码)_kaic
spring boot·后端·宠物
Java小白中的菜鸟3 小时前
centos7的磁盘扩容
linux·运维·服务器
黑客老陈4 小时前
面试经验分享 | 北京渗透测试岗位
运维·服务器·经验分享·安全·web安全·面试·职场和发展
橘子师兄4 小时前
如何在自己的云服务器上部署mysql
运维·服务器·mysql
@泽栖4 小时前
Docker 常用命令
运维·服务器·docker