环境:Springboot2.4.11概述为什么要创建SpringWebFlux?部分答案是需要一个非阻塞的web堆栈来处理线程数较少的并发,并使用较少的Expand硬件资源。Servlet3.1确实为非阻塞I/O提供了一个API。但是,使用它可能会导致ServletAPI的其他部分出现偏差。这是一个新的公共API成为任何非阻塞运行时基础的动机。这很重要,因为服务器(如Netty)在异步、非阻塞空间中得到很好的建立。另一部分答案是函数式编程。正如在Java5中添加注释创造了机会(例如带注释的REST控制器或单元测试)一样,在Java8中添加lambda表达式也为Java中的函数式API创造了机会。这对于允许异步逻辑的声明式组??合的非阻塞应用程序和延续式API(由CompletableFuture和ReactiveX推广)是一个福音。在编程模型级别,Java8支持SpringWebFlux以提供功能性Web端点和带注释的控制器。什么是反应式编程?“反应式”是指围绕对变化做出反应而构建的编程模型——网络组件响应I/O事件、UI控制器响应鼠标事件,以及其他组件。非阻塞是反应性的,因为我们现在不是被阻塞,而是在操作完成或数据可用时对通知做出反应。在Spring团队中,还有另一个重要的机制与“反应性”相关联,那就是非阻塞背压。在同步命令式代码中,阻塞调用作为背压的一种自然形式,迫使调用者等待。在非阻塞代码中,控制事件发生率变得非常重要,这样快速的生产者就不会淹没他们的目的地。ReactiveStreams是一个小型规范(在Java9中也采用),它定义了具有背压的异步组件之间的交互。例如,数据存储库(作为发布者)可以生成HTTP服务器(作为订阅者)可以写入响应的数据。反应流的主要目的是让订阅者控制发布者产生数据的速率。ReactiveStreams在互操作性方面发挥着重要作用。它对库和基础结构组件很感兴趣,但作为应用程序API不是很有用,因为它太低级了。应用程序需要更高级别、更丰富、更强大的API来组合异步逻辑——类似于Java8StreamsAPI,但不仅仅是用于集合。这就是反应式库所扮演的角色。Reactor是SpringWebFlux的首选响应式库。它提供Mono和FluxAPI类型,通过一组与ReactiveX运算符词汇表一致的丰富运算符来处理0..1(Mono)和0..N(Flux)数据序列。Reactor是一个响应式流库,因此,它的所有操作符都支持非阻塞背压。Reactor非常专注于服务器端Java。它是与Spring密切合作开发的。WebFlux需要Reactor作为核心依赖,但它可以通过反应流与其他反应库进行互操作。作为一般规则,WebFluxAPI接受普通发布者作为输入,在内部将其调整为反应器类型,使用该类型,并返回Flux或Mono作为输出。编程模型SpringWeb模块包含SpringWebFlux的基础,包括HTTP抽象、用于服务器支持的反应流适配器、编解码器和与ServletAPI相当但具有非阻塞契约的核心WebHandler-API。在此基础上,SpringWebFlux提供了两种编程模型的选择:AnnotatedControllers:与SpringMVC一致,基于SpringWeb模块中相同的注解。SpringMVC和WebFlux控制器都支持响应式(Reactor和RxJava)返回类型,因此很难区分它们。一个显着的区别是WebFlux还支持响应式@RequestBody参数。FunctionalEndpoints:基于Lambda的轻量级函数式编程模型。您可以将其视为应用程序可用于路由和处理请求的小型库或实用程序集。与注解控制器最大的区别是应用程序负责从开始到结束的请求处理,而不是通过注解声明意图并被回调。适用性SpringMVC和WebFlux如何选择?它们可以并排使用,各方的反馈对双方都有利。下图显示了两者之间的关系、它们的共同点以及各自独特支持的内容:我们建议您考虑以下几点:如果您有一个运行良好的SpringMVC应用程序,则无需更改它。命令式编程是编写、理解和调试代码的最简单方法。您可以选择尽可能多的库,因为从历史上看,它们中的大多数都是阻塞的。如果您已经购买了非阻塞Web堆栈,SpringWebFlux提供与该领域其他产品相同的执行模型优势,以及服务器选择(Netty、Tomcat、Jetty、Undertow和Servlet3.1+容器)、选择编程模型(带注释的控制器和功能性Web端点),以及可选的反应式库(Reactor、RxJava或其他)。如果您对Java8Lambdas或Kotlin的轻量级功能性Web框架感兴趣,您可以使用SpringWebFlux功能性Web端点。对于要求不那么复杂的小型应用程序或微服务来说,这也是一个不错的选择,因为它们可以从更高的透明度和控制中获益。在微服务架构中,可以混合使用具有SpringMVC或SpringWebFlux控制器或SpringWebFlux功能端点的应用程序。两个框架都支持相同的基于注解的编程模型,从而更容易重用知识,同时还可以为正确的工作选择正确的工具。评估应用程序的一种简单方法是检查其依赖项。如果您有块持久性API(JPA、JDBC)或网络API供您使用,那么SpringMVC至少是通用架构的最佳选择。在技??术上可以使用Reactor和RxJava在单独的线程上进行阻塞调用,但您无法充分利用非阻塞Web堆栈。如果您有调用远程服务的SpringMVC应用程序,请尝试使用ReactiveWebClient。您可以直接从SpringMVC控制器方法返回反应类型(Reactor、RxJava或其他)。每次调用的延迟越大或调用之间的相互依赖性越大,好处就越显着。SpringMVC控制器也可以调用其他React组件。如果您有一个大型团队,请记住,在向非阻塞、函数式和声明式编程过渡的过程中有一个陡峭的学习曲线。在没有完全转换的情况下开始的一种实用方法是使用反应式WebClient。除此之外,从小处着手并衡量收益。我们预计,对于广泛的应用,这种转变将是不必要的。如果您不确定要寻找哪些好处,请先了解非阻塞I/O的工作原理(例如,单线程Node.js上的并发性)及其影响。应用程序服务SpringWebFlux在Tomcat、Jetty、Servlet3.1+容器和非Servlet运行时(如Netty和Undertow)上受支持。所有服务器都在一个低级通用API上工作,以支持跨服务器的高级编程模型。SpringWebFlux没有对启动或停止服务器的内置支持。但是,很容易从Spring配置和WebFlux基础设施组装一个应用程序并使用几行代码运行它。SpringBoot有一个WebFluxstarter可以自动执行这些步骤。初学者默认使用Netty,但通过更改Maven或Gradle依赖项可以轻松切换到Tomcat、Jetty或Undertow。springboot默认使用Netty,因为它更广泛的用于异步,非阻塞,允许client和server共享资源。Tomcat和Jetty可以与SpringMVC和WebFlux一起使用。但是,请记住它们的用法非常不同。SpringMVC依靠Servlet来阻塞I/O,并允许应用程序在需要时直接使用ServletAPI。SpringWebFlux依赖于Servlet3.1非阻塞I/O,并在低级适配器后面使用ServletAPI。它不暴露以供直接使用。对于Undertow,SpringWebFlux直接使用UndertowAPI而不是ServletAPI。性能性能有很多特点和含义。反应式和非阻塞通常不会使应用程序运行得更快。在某些情况下,它们可以(例如,如果使用WebClient并行运行远程调用)。总的来说,以非阻塞方式做事需要更多的工作,这会稍微增加所需的处理时间。反应式和非阻塞的主要预期好处是能够使用少量固定线程和更少内存进行扩展。这使得应用程序在负载下更具弹性,因为它们以更可预测的方式扩展。然而,为了观察这些好处,您需要有一些延迟(包括缓慢且不可预测的网络I/O混合)。这是反应式堆栈开始显示其优势的地方,差异可能是巨大的。线程模型在SpringMVC(以及一般的servlet应用程序)中,假定应用程序可以阻塞当前线程(例如,远程调用)。出于这个原因,servlet容器使用一个大线程池来吸收请求处理期间潜在的阻塞。在SpringWebFlux(以及一般的非阻塞服务器)中,假定应用程序不会阻塞。因此,非阻塞服务器使用一个小的、固定大小的线程池(事件循环工作者)来处理请求。调用阻塞API如果您真的需要使用阻塞库怎么办?Reactor和RxJava都提供了publishOn操作符来在不同的线程上继续处理。这意味着有一个简单的逃生口。但是,请记住阻塞API不适合这种并发模型。可变状态在Reactor和RxJava中,逻辑是通过运算符声明的。在运行时,形成一个反应管道,其中数据在不同阶段按顺序处理。这样做的一个主要好处是它使应用程序不必保护可变状态,因为永远不会同时调用此管道中的应用程序代码。线程模型在运行SpringWebFlux的服务器上,您希望看到哪些线程?在“正常”的SpringWebFlux服务器上(例如,没有数据访问或其他可选依赖项),您可以期望服务器有一个线程和几个其他线程用于请求处理(通常是相同数量的CPU内核)。但是,Servlet容器可以从更多线程开始(例如,Tomcat上有10个线程)以支持Servlet(阻塞)I/O和Servlet3.1(非阻塞)I/O使用。ReactiveWebClient在事件循环中运行。所以你可以看到与此相关的少量固定处理线程(例如reactorhttpnio-带有reactornetty连接器)。但是,如果ReactorNetty用于客户端和服务器,默认情况下两个服务器共享事件循环资源。Reactor和RxJava提供了一个线程池抽象,称为调度器,它与publishOn运算符一起使用,用于将处理切换到不同的线程池。调度程序的名称表示特定的并发策略——例如,“parallel”(用于CPU绑定、线程绑定工作)或“elastic”(用于I/O绑定、线程绑定工作)。如果您看到这样的线程,则意味着某些代码正在使用特定的线程池调度程序策略。数据访问库和其他第三方依赖项也可以创建和使用自己的线程。引入依赖
