此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

概述

为什么要创建 Spring WebFlux?spring-doc.cadn.net.cn

部分答案是需要一个非阻塞 Web 堆栈来处理并发性 线程数少,以更少的硬件资源进行扩展。Servlet 非阻塞 I/O 导致远离 Servlet API 的其余部分,其中合约是同步的 (Filter,Servlet) 或阻塞 (getParameter,getPart).这就是动机 新的通用 API 作为任何非阻塞运行时的基础。那是 重要,因为服务器(例如 Netty)在异步中已经建立得很好, 无阻塞空间。spring-doc.cadn.net.cn

答案的另一部分是函数式编程。就像添加注释一样 在 Java 5 创建的机会(例如带注释的 REST 控制器或单元测试)中,该 在 Java 8 中添加 lambda 表达式为 Java 中的函数式 API 创造了机会。 这对于非阻塞应用程序和延续式 API(如普及的那样)来说是一个福音 由CompletableFutureReactiveX),允许声明式 异步逻辑的组成。在编程模型级别,Java 8 启用了 Spring WebFlux 提供功能性 Web 端点以及带注释的控制器。spring-doc.cadn.net.cn

定义“响应式”

我们谈到了“非阻塞”和“功能性”,但响应式是什么意思?spring-doc.cadn.net.cn

术语“响应式”是指围绕对变化做出反应而构建的编程模型——对 I/O 事件做出反应的网络组件、对鼠标事件做出反应的 UI 控制器等等。 从这个意义上说,非阻塞是被动的,因为我们现在不是被阻塞,而是处于模式 在作完成或数据可用时对通知做出反应。spring-doc.cadn.net.cn

我们还 Spring 团队将另一个重要机制与“响应式”联系在一起 这就是非阻塞背压。在同步命令式代码中,阻止调用 作为一种自然形式的背压,迫使呼叫者等待。在非阻塞中 代码中,控制事件的速率变得很重要,这样快速生产者就不会 压倒了它的目的地。spring-doc.cadn.net.cn

Reactive Streams 是一个小规范(在 Java 9 中也采用) 这定义了异步组件与背压之间的交互。 例如,数据存储库(充当发布者) 可以生成 HTTP 服务器(充当订阅者)的数据 然后可以写入响应。Reactive Streams 的主要目的是让 订阅者控制发布者生成数据的速度或速度。spring-doc.cadn.net.cn

常见问题:如果发布商不能放慢速度怎么办?
反应流的目的只是建立机制和边界。 如果发布者无法减慢速度,则必须决定是缓冲、丢弃还是失败。

响应式 API

反应流在互作性方面发挥着重要作用。图书馆对此感兴趣 和基础设施组件,但作为应用程序 API 不太有用,因为它太有用了 低级。应用程序需要更高级别、更丰富的功能 API 来 compose 异步逻辑 — 类似于 Java 8StreamAPI,但不仅适用于集合。 这就是响应式库所扮演的角色。spring-doc.cadn.net.cn

Reactor 是首选的反应性库 Spring WebFlux 的 WebFlux。它提供了MonoFluxAPI 类型 处理 0..1 (Mono) 和 0..N (Flux)通过一组丰富的运算符与 ReactiveX 运算符词汇表。 Reactor 是一个 Reactive Streams 库,因此,它的所有运算符都支持非阻塞背压。 Reactor 非常注重服务器端 Java。它是在密切合作下开发的 与Spring。spring-doc.cadn.net.cn

WebFlux 需要 Reactor 作为核心依赖项,但它可以与其他响应式互作 通过反应流的库。作为一般规则,WebFlux API 接受普通Publisher作为输入,在内部将其适配为 Reactor 类型,使用它,并返回FluxMono作为输出。因此,您可以传递任何Publisher作为输入,您可以应用 作,但您需要调整输出以与另一个响应式库一起使用。 只要可行(例如,带注释的控制器),WebFlux 就会透明地适应使用 RxJava 或其他响应式库。有关更多详细信息,请参阅响应式库spring-doc.cadn.net.cn

除了响应式 API 之外,WebFlux 还可以与 Kotlin 中的协程 API 一起使用,从而提供更命令性的编程风格。 协程 API 将提供以下 Kotlin 代码示例。

编程模型

spring-web模块包含作为 Spring WebFlux 基础的响应式基础, 包括 HTTP 抽象、支持服务器的响应式流适配器编解码器和内核WebHandler应用程序接口可与 Servlet API,但具有非阻塞合约。spring-doc.cadn.net.cn

在此基础上,Spring WebFlux 提供了两种编程模型的选择:spring-doc.cadn.net.cn

  • 带注释的控制器:与 Spring MVC 一致,并基于 来自spring-web模块。Spring MVC 和 WebFlux 控制器都支持响应式 (Reactor 和 RxJava) 返回类型,因此,很难区分它们。一个值得注意的 不同之处在于 WebFlux 还支持响应式@RequestBody参数。spring-doc.cadn.net.cn

  • 功能端点:基于 Lambda、轻量级、 和函数式编程模型。您可以将其视为一个小型库或一组 应用程序可用于路由和处理请求的实用程序。最大的区别 带注释的控制器是应用程序负责请求处理 从头到尾,而不是通过注释声明意图并被回调。spring-doc.cadn.net.cn

适用性

Spring MVC 还是 WebFlux?spring-doc.cadn.net.cn

这是一个很自然的问题,但却设置了一个不健全的二分法。实际上,两者 共同努力,扩大可用选项的范围。两者专为 彼此的连续性和一致性,它们并排可用,并且反馈 双方都受益。下图显示了两者的关系,它们是什么 有共同点,以及每个共同点支持的内容:spring-doc.cadn.net.cn

Spring MVC 和 WebFlux 维恩

我们建议您考虑以下具体要点:spring-doc.cadn.net.cn

  • 如果你有一个运行良好的 Spring MVC 应用程序,则无需更改。 命令式编程是编写、理解和调试代码的最简单方法。 您可以最大限度地选择库,因为从历史上看,大多数库都是阻塞的。spring-doc.cadn.net.cn

  • 如果您已经在购买非阻塞 Web 堆栈,Spring WebFlux 提供相同的功能 执行模型与该领域的其他模型一样受益,并且还提供了服务器的选择 (Netty、Tomcat、Jetty、Undertow 和 Servlet 容器),可选择编程模型 (带注释的控制器和功能性 Web 端点),以及响应式库的选择 (Reactor、RxJava 或其他)。spring-doc.cadn.net.cn

  • 如果您对与 Java 8 lambda 一起使用的轻量级、功能齐全的 Web 框架感兴趣 或 Kotlin,您可以使用 Spring WebFlux 功能 Web 端点。这也是一个不错的选择 适用于要求不太复杂的小型应用程序或微服务,可以受益 来自更大的透明度和控制。spring-doc.cadn.net.cn

  • 在微服务架构中,您可以混合使用具有 Spring MVC 的应用程序 或 Spring WebFlux 控制器或 Spring WebFlux 功能端点。获得支持 对于两个框架中相同的基于注释的编程模型,可以更轻松地 重复使用知识,同时为正确的工作选择正确的工具。spring-doc.cadn.net.cn

  • 评估应用程序的一种简单方法是检查其依赖关系。如果您有阻止 持久化 API(JPA、JDBC)或网络 API 使用,Spring MVC 是最佳选择 至少对于通用架构来说是这样。它在技术上是可行的,无论是 Reactor 还是 RxJava 在单独的线程上执行阻塞调用,但您不会将 大部分是非阻塞 Web 堆栈。spring-doc.cadn.net.cn

  • 如果你有一个调用远程服务的 Spring MVC 应用程序,请尝试响应式WebClient. 您可以返回响应式类型(Reactor、RxJava 或其他) 直接来自 Spring MVC 控制器方法。每次调用的延迟越大,或者 呼叫之间的相互依赖性,好处就越显着。Spring MVC 控制器 也可以调用其他响应式组件。spring-doc.cadn.net.cn

  • 如果您有一个庞大的团队,请记住向非阻塞转变的陡峭学习曲线, 函数式编程和声明式编程。无需全开关即可启动的实用方法 就是用响应式WebClient.除此之外,从小处着手并衡量收益。 我们预计,对于广泛的应用,这种转变是不必要的。如果你是 不确定要寻找什么好处,请从了解非阻塞 I/O 的工作原理开始 (例如,单线程Node.js上的并发性)及其效果。spring-doc.cadn.net.cn

服务器

Spring WebFlux 在 Tomcat、Jetty、Servlet 容器以及非 Servlet 运行时(例如 Netty 和 Undertow)上受支持。所有服务器都适应了低级的通用 API,以便可以跨服务器支持更高级别的编程模型spring-doc.cadn.net.cn

Spring WebFlux 没有启动或停止服务器的内置支持。但是,它是很容易从 Spring 配置和 WebFlux 基础设施组装应用程序,并用几行代码运行代码行。spring-doc.cadn.net.cn

Spring Boot 有一个 WebFlux Starters,可以自动执行这些步骤。默认情况下,Starters使用 Netty,但通过更改 Maven 或 Gradle 依赖项。Spring Boot 默认为 Netty,因为它更广泛 用于异步、非阻塞空间,并允许客户端和服务器共享资源。spring-doc.cadn.net.cn

Tomcat 和 Jetty 可以与 Spring MVC 和 WebFlux 一起使用。但是请记住, 它们的使用方式非常不同。Spring MVC 依赖于 Servlet 阻塞 I/O 和 允许应用程序在需要时直接使用 Servlet API。Spring WebFlux 依赖于 Servlet 非阻塞 I/O,并在低级后面使用 Servlet API 适配器。它不暴露用于直接使用。spring-doc.cadn.net.cn

强烈建议不要在 WebFlux 应用程序的上下文中映射 Servlet 过滤器或直接作 Servlet API。 由于上面列出的原因,在同一上下文中混合阻塞 I/O 和非阻塞 I/O 将导致运行时问题。

对于 Undertow,Spring WebFlux 直接使用 Undertow API,而无需 Servlet API。spring-doc.cadn.net.cn

性能

性能有很多特征和意义。一般响应式和非阻塞性 不要让应用程序运行得更快。在某些情况下,它们可以 - 例如,如果使用WebClient并行运行远程呼叫。但是,它需要做更多的工作 以非阻塞方式进行,这会稍微增加所需的处理时间。spring-doc.cadn.net.cn

响应式和非阻塞的关键预期好处是能够通过小的、 固定线程数和更少的内存。这使得应用程序在负载下更具弹性, 因为它们以更可预测的方式扩展。然而,为了观察这些好处,你 需要有一些延迟(包括缓慢和不可预测的网络 I/O 的混合)。 这就是响应式堆栈开始展示其优势的地方,差异可能是 戏剧性的。spring-doc.cadn.net.cn

并发模型

Spring MVC 和 Spring WebFlux 都支持带注释的控制器,但有一个键 并发模型的差异以及阻塞和线程的默认假设。spring-doc.cadn.net.cn

在 Spring MVC(以及一般的 servlet 应用程序)中,假设应用程序可以 阻止当前线程(例如,对于远程调用)。因此,servlet 容器 使用大型线程池来吸收请求处理期间的潜在阻塞。spring-doc.cadn.net.cn

在 Spring WebFlux(以及一般的非阻塞服务器)中,假设应用程序 不要阻塞。因此,非阻塞服务器使用小型、固定大小的线程池 (事件循环工作程序)来处理请求。spring-doc.cadn.net.cn

“缩放”和“线程数量少”听起来可能很矛盾,但永远不要阻止 当前线程(并依赖回调)意味着您不需要额外的线程,因为 没有要吸收的阻止呼叫。

调用阻塞 API

如果您确实需要使用阻止库怎么办?Reactor 和 RxJava 都提供了publishOn运算符以继续在不同的线程上进行处理。这意味着有一个 轻松逃生舱口。但是请记住,阻止 API 并不适合 这种并发模型。spring-doc.cadn.net.cn

可变状态

在 Reactor 和 RxJava 中,你通过运算符声明逻辑。在运行时,响应式 管道是在不同阶段按顺序处理数据的地方形成的。一个主要优势 其中,它使应用程序不必保护可变状态,因为 该管道中的应用程序代码永远不会同时调用。spring-doc.cadn.net.cn

线程模型

您应该期望在运行 Spring WebFlux 的服务器上看到哪些线程?spring-doc.cadn.net.cn

  • 在“普通”Spring WebFlux 服务器上(例如,没有数据访问或其他可选的 dependencies),您可以期望服务器有一个线程,请求需要其他几个线程 处理(通常与 CPU 内核数一样多)。然而,Servlet 容器 可以从更多线程开始(例如,Tomcat 上的 10 个线程),以支持 servlet(阻塞)I/O 和 servlet 3.1(非阻塞)I/O 使用。spring-doc.cadn.net.cn

  • 响应式WebClient以事件循环样式运行。所以你可以看到一个小的,固定的 与之相关的处理线程数(例如,reactor-http-nio-与反应堆 Netty 连接器)。但是,如果 Reactor Netty 同时用于客户端和服务器,则 默认共享事件循环资源。spring-doc.cadn.net.cn

  • Reactor 和 RxJava 提供了线程池抽象,称为调度器,可与publishOn运算符,用于将处理切换到不同的线程池。调度程序的名称建议特定的并发策略——例如,“并行”(对于线程数量有限的 CPU 绑定工作)或“弹性”(用于大量线程的 I/O 绑定工作)。如果您看到此类线程,则意味着某些代码正在使用特定线程池Scheduler策略。spring-doc.cadn.net.cn

  • 数据访问库和其他第三方依赖项也可以创建和使用线程他们自己的。spring-doc.cadn.net.cn

配置

Spring Framework 不提供对启动和停止服务器的支持。要为服务器配置线程模型,您需要使用特定于服务器的配置 API,或者,如果您使用 Spring Boot,检查每个服务器的 Spring Boot 配置选项。您可以配置WebClient径直。 对于所有其他库,请参阅其各自的文档。spring-doc.cadn.net.cn