|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
概述
春季网络流为何诞生?
部分原因是需要一个非阻塞的网络栈来处理并发少量线程,并以更少的硬件资源实现扩展。Servlet 非阻塞 I/O使其远离 Servlet API 的其他部分,后者合同是同步的 (Filter,servlet)或阻挡(getParameter,getPart). 这就是推动建立一个新的通用 API 作为跨越任何非阻塞运行时的基础。 那是 重要原因是有服务器(如Netty)在异步空间中已稳固建立,非阻塞空间。
答案的另一部分是函数式编程。就像注释的添加一样Java 5 创造了机会(如带注释的 REST 控制器或单元测试),Java 8 中lambda 表达式的加入为 Java 功能性 API 创造了机会。这对非阻塞应用和延续式 API(如其普及)是一大福音 由完成未来以及ReactiveX)支持声明式异步逻辑的组合。在编程模型层面,Java 8使SpringWebFlux能够提供功能齐全的Web端点以及带注释的控制器。
定义“被动型”吗?
我们提到了“非阻塞”和“功能性”,但反应性到底是什么意思?
“响应式”一词指的是围绕响应变化构建的编程模型——网络组件响应I/O事件,UI控制器响应鼠标事件等。从这个意义上说,非阻塞是被动的,因为我们不再被阻挡,而是处于当作完成或数据可用时响应通知的模式。
我们还有一个重要机制,我们Spring团队将“反应性”联系在一起那就是非阻塞性反压。在同步、命令式代码中,阻断呼叫作为一种自然的背压形式,迫使呼叫者等待。在非阻塞性代码中,控制事件速率变得非常重要,这样快速的生产者不会压倒其目的地。
响应式流是一个小型规范(也被Java 9采纳)它定义了异步组件之间带有反压的交互。例如,一个数据存储库(作为发布者)可以产生HTTP服务器(作为订阅者)能够写入响应的数据。然后可以写入响应。响应式流的主要目的是让订阅者控制发布者生成数据的速度或速度。
|
常见问题:如果发布者无法放慢速度怎么办? 响应式流的目的仅是建立机制和界限。如果发布者无法放慢速度,就必须决定是缓冲、丢弃还是失败。 |
响应式API
响应式流在互作性方面起着重要作用。它对图书馆具有重要意义
以及基础设施组件,但作为应用API的实用性较低,因为它太过于
低级。应用程序需要更高级、更丰富、功能更丰富的 API 来实现
comp async 逻辑——类似于 Java 8流API 但不仅仅是集合。
这正是响应式库所扮演的角色。
反应器是 的首选响应式库
春季网络流。它提供了单和通量API 类型
用于处理0..1的数据序列(单)和0..N(通量)通过一组丰富的算子,与
ReactiveX 运算符词汇。
反应器是一个反应流库,因此其所有操作员都支持非阻断背压。
Reactor 非常专注于服务器端 Java。该项目在密切合作下开发
和Spring一起。
WebFlux 需要 Reactor 作为核心依赖,但它可以与其他响应式设备互作
通过反应流(Reactive Streams)获取图书馆。一般来说,WebFlux API 接受发行人作为输入,将其内部调整为反应堆类型,使用该类型,返回通量或者单作为输出。所以,你可以通过任何发行人作为输入,你可以应用
但你需要将输出调整以配合另一个响应式库。
在可行的情况下(例如,带注释的控制器),WebFlux 会透明地适应这种使用情况
RxJava 或其他响应式库。详情请参见响应式库。
| 除了响应式 API,WebFlux 还可以与 Kotlin 中的协程 API 一起使用,这提供了更具命令式的编程风格。 以下 Kotlin 代码示例将与协程 API 一同提供。 |
编程模型
这春网模块包含了Spring WebFlux的响应式基础,
包括HTTP抽象、响应式流适配器
服务器、编解码器和核心WebHandler应用程序接口可与
Servlet API 但采用了非阻塞的契约。
基于此,Spring WebFlux 提供了两种编程模式的选择:
适用性
春季MVC还是WebFlux?
这是一个自然而然的问题,但却造成了一个不合理的二分法。其实,两者都有 共同努力,扩大可选方案的范围。这两者设计为 它们之间的连续性和一致性是并存的,并且可以获得反馈 双方都受益。下图展示了两者的关系及其关系 有共同点,以及各自独特的支持点:
我们建议您考虑以下具体要点:
-
如果你的 Spring MVC 应用运行良好,就没必要更改。 命令式编程是编写、理解和调试代码最简单的方法。 你可以最大限度地选择库,因为历史上大多数库都是封锁的。
-
如果你已经在寻找非阻塞的网页栈,Spring WebFlux 也提供类似的服务 执行模型与该领域的其他模型相比更有利,同时也提供了服务器选择 (Netty、Tomcat、Jetty、Undertow 和 Servlet 容器),编程模型的选择 (注释控制器和功能性网页端点),以及响应式库的选择 (Reactor、RxJava 或其他)
-
如果你对一个轻量级、功能丰富的Web框架感兴趣,用于Java 8 lambda或者Kotlin,你可以使用Spring WebFlux的功能性网页端点。这也是一个不错的选择对于较小的应用或需求较少的微服务,这些需求更为复杂,可以受益于更高的透明度和控制力。
-
在微服务架构中,你可以混合使用Spring MVC或Spring WebFlux控制器,或Spring WebFlux功能端点。支持在两个框架中支持相同的基于注释的编程模型,使得在选择合适的工具的同时,重新利用知识,以完成正确的工作。
-
评估应用的一个简单方法是检查其依赖关系。如果你有阻断持久化API(JPA、JDBC)或网络API可用,Spring MVC是最佳选择至少对于常见架构来说是这样。技术上,在Reactor和RxJava上都可以在独立线程上执行阻断调用,但你不会充分利用一个非阻断的网络栈。
-
如果你有带有远程服务调用的 Spring MVC 应用,可以试试被动式
Web客户端. 你可以返回反应类型(Reactor、RxJava 或其他)直接来自 Spring MVC 控制器方法。每次调用的延迟越大,或者调用之间的相互依赖性越大,收益就越显著。Spring MVC 控制器也可以调用其他响应式组件。 -
如果你的团队很大,请记住转向非阻塞编程时的陡峭学习曲线,函数式和声明式编程。一个实用的开始方式是不完全切换是使用响应式编程
Web客户端. 除此之外,从小做起,衡量收益。我们预计,对于各种应用来说,这种转变是不必要的。如果你不确定该寻找哪些好处,首先了解非阻塞I/O的工作原理(例如,单线程Node.js上的并发)及其影响。
服务器
Spring WebFlux 支持于 Tomcat、Jetty、Servlet 容器,以及非 Servlet 运行时如 Netty 和 Undertow。所有服务器都适配到低级别的通用 API,以便支持更高级的编程模型跨服务器。
Spring WebFlux 没有内置启动或停止服务器的支持。然而,从 Spring 配置和 WebFlux 基础设施组装应用程序并运行起来非常简单只需几行代码。
Spring Boot 有一个 WebFlux 启动程序,可以自动化这些步骤。默认情况下,启动程序使用Netty,但通过更改你的Maven 或 Gradle 依赖,切换到 Tomcat、Jetty 或 Undertow 也很容易。Spring Boot 默认使用 Netty,因为它更广泛用于异步、非阻塞空间,允许客户端和服务器共享资源。
Tomcat 和 Jetty 可以同时使用 Spring MVC 和 WebFlux。但请记住它们的使用方式非常不同。Spring MVC 依赖于 Servlet 阻断输入输出,并且让应用程序在需要时直接使用 Servlet API。Spring WebFlux依赖于 Servlet 非阻塞 I/O,并在低层次使用 Servlet API 适配器。 它不会直接暴露在外。
| 强烈建议不要在WebFlux应用的上下文中映射Servlet过滤器或直接作Servlet API。基于上述原因,在同一上下文中混合阻塞I/O和非阻塞I/O会导致运行时问题。 |
对于 Undertow,Spring WebFlux 直接使用 Undertow API,而非 Servlet API。
性能
性能具有许多特性和含义。响应式和非阻塞性通常不会让应用程序运行更快。在某些情况下,它们可以——例如,如果使用Web客户端并行运行远程通话。不过,这需要更多的工作
用非阻塞的方式处理,这会稍微增加处理时间。
响应式和非阻塞性的关键预期优势是能够以小规模的 线程数量固定,内存更少。这使应用程序在负载下更具韧性, 因为它们的规模更可预测。然而,为了享受这些好处,你必须 需要有一定的延迟(包括缓慢且不可预测的网络I/O混合)。 这正是响应式堆栈开始展现其优势的地方,差异可以 戏剧性的。
并发模型
Spring MVC 和 Spring WebFlux 都支持带注释的控制器,但有一个密钥 并发模型与阻塞和线程默认假设的差异。
在 Spring MVC(以及一般的 servlet 应用)中,假设应用程序可以 阻断当前线程,(例如,用于远程调用)。因此,servlet 容器 使用大型线程池来吸收请求处理过程中潜在的阻塞。
在 Spring WebFlux(以及一般非阻塞服务器)中,假设应用程序 不要拉黑。因此,非阻塞服务器使用一个小型、固定大小的线程池 (事件循环工作人员)处理请求。
| “按比例”和“线数少”听起来可能矛盾,但永远不要阻挡 当前线程(并且依赖回调)意味着你不需要额外的线程,因为 没有阻挡电话需要承受。 |
可变状态
在 Reactor 和 RxJava 中,你通过运算符声明逻辑。运行时,是响应式的 流水线形成时,数据按顺序、不同阶段进行处理。一个关键的好处 其中之一是它使应用程序无需保护可变状态,因为 该流水线中的应用程序代码绝不会同时调用。
线程模型
运行 Spring WebFlux 的服务器上应该预期会看到哪些线程?
-
在“原版”Spring WebFlux服务器上(例如,没有数据访问或其他可选功能 依赖关系),你可以预期服务器端有一个线程,请求线程则有好几个线程 处理(通常与CPU核心数相当)。然而,servlet 容器, 可能从更多线程开始(例如Tomcat上有10个线程),以支持Servlet(阻塞)I/O两种 以及servlet 3.1(非阻塞)I/O使用。
-
响应式
Web客户端采用事件循环方式运行。所以你可以看到一个小而固定的 与该数据相关的处理线程数量(例如,reactor-http-nio-与反应堆合作 Netty连接器)。然而,如果Reactor Netty同时用于客户端和服务器,则两者 默认共享事件循环资源。 -
Reactor 和 RxJava 提供了线程池抽象,称为调度器,用于
发布用于切换处理到不同线程池的作符。 调度器名称暗示了特定的并发策略——例如,“并行” (针对线程数量有限的CPU工作)或“弹性”(用于I/O受限的工作,且 大量线程)。如果你看到这样的线程,说明某些代码正在使用 特定线程池调度策略。 -
数据访问库和其他第三方依赖也可以创建和使用 thread 他们自己的。