Spring WebFlux详解

news2024/11/28 2:35:45

Spring 框架中包含的原始 Web 框架 Spring Web MVC 是专门为 Servlet API 和 Servlet 容器而设计的。后来在 5.0 版本中加入了 reactive 栈的 Web 框架 Spring WebFlux。它是完全非阻塞的,支持 Reactive Streams 背压,并在 Netty、Undertow 和 Servlet 容器等服务器上运行。

这两个Web框架都反映了其源模块的名称( spring-webmvc 和 spring-webflux)并在Spring框架中并存。每个模块都是可选的。应用程序可以使用一个或另一个模块,或者在某些情况下同时使用—​例如,Spring MVC Controller 与响应式 WebClient。

一、 概览

为什么要创建 Spring WebFlux?

部分答案是需要一个非阻塞的Web栈来处理少量线程的并发,并以较少的硬件资源进行扩展。Servlet 的非阻塞I/O引出了Servlet API的其他部分,其中的约定是同步的(Filter,Servlet)或阻塞的(getParameter,getPart)。这是一个新的通用API的动机,作为跨越任何非阻塞运行时的基础。这一点很重要,因为服务器(如Netty)在异步、非阻塞领域已经很成熟了。

答案的另一部分是函数式编程。就像Java 5中增加的注解创造了机会(如注解的REST controller 或单元测试)一样,Java 8中增加的lambda表达式为Java中的函数式 API 创造了机会。这对非阻塞式应用和 continuation 式API(如 CompletableFuture 和 ReactiveX 所推广的)来说是个福音,它们允许异步逻辑的声明式组合。在编程模型层面,Java 8 使 Spring WebFlux 能够在注解的 controller 的同时提供函数式的 Web 端点。

二、定义 “Reactive” (响应式)

我们谈到了 "非阻塞" 和 "函数式",但响应式是什么意思?

术语 “reactive”(响应式), 指的是围绕对变化做出反应的编程模型—​对I/O事件做出反应的网络组件,对鼠标事件做出反应的UI controller,以及其他。从这个意义上说,非阻塞就是响应式,因为,我们现在不是被阻塞,而是在操作完成或数据可用时对通知做出反(响)应。

还有一个重要的机制,我们Spring团队将其与 "响应式" 联系在一起,那就是非阻塞式背压。在同步的、命令式的代码中,阻塞式调用是一种自然的背压形式,迫使调用者等待。在非阻塞代码中,控制事件的速度变得很重要,这样快速的生产者就不会压倒其“消费者”。

Reactive Streams是一个 小型规范(也在Java 9中 采用),它定义了异步组件之间的互动,具有背压。例如,一个 data repository(作为 Publisher)可以产生数据,然后由一个HTTP服务器(作为 Subscriber)写入响应。Reactive Streams 的主要目的是让订阅者控制发布者产生数据的快慢。

常见的问题:如果 publisher 不能放慢速度怎么办?
Reactive Stream 的目的只是为了建立机制和一个边界。如果一个 publisher 不能放慢速度,它必须决定是缓冲、放弃还是失败。

三、Reactive API

Reactive Streams 对互操作性起着重要作用。它对库和基础设施组件很有意义,但作为应用程序的API却不太有用,因为它太低级了。应用程序需要一个更高级别的、更丰富的、功能性的API来组成异步逻辑—​类似于Java 8的 Stream API,但不只是用于集合。这就是响应式库所扮演的角色。

Reactor 是Spring WebFlux 的首选响应式库。它提供了 Mono 和 Flux API类型,通过与 ReactiveX 运算符词汇 相一致的丰富运算符集,对 0..1(Mono)和 0..N(Flux)的数据序列进行操作。Reactor是一个Reactive Streams库,因此,它的所有操作符都支持非阻塞的背压。Reactor非常关注服务器端的Java。它是与Spring紧密合作开发的。

WebFlux需要Reactor作为核心依赖,但它可以通过Reactive Streams与其他响应式库互操作。一般来说,WebFlux API接受一个普通的 Publisher 作为输入,在内部将其调整为Reactor类型,使用该类型,并返回 Flux 或 Mono 作为输出。因此,你可以传递任何 Publisher 作为输入,你可以在输出上应用操作,但你需要调整输出,以便与另一个响应式库一起使用。在可行的情况下(例如,注解 controller),WebFlux会透明地适应RxJava或其他响应式库的使用。更多细节请参见 响应式库。

除了Reactive APIs,WebFlux还可以与Kotlin中的 Coroutines APIs一起使用,它提供了一种更多的命令式编程风格。下面的Kotlin代码样本将提供Coroutines APIs。

四、编程模型

spring-web 模块包含了支撑Spring WebFlux的响应式基础,包括HTTP抽象、支持服务器的Reactive Streams 适配器、编解码器,以及与Servlet API相当但具有非阻塞约定的核心 WebHandler API。

在此基础上,Spring WebFlux提供了两种编程模型的选择:

  • 注解式 Controller: 与Spring MVC一致,基于 spring-web 模块的相同注解。Spring MVC 和 WebFlux Controller 都支持响应式(Reactor和RxJava)返回类型,因此,要区分它们并不容易。一个明显的区别是,WebFlux也支持响应式的 @RequestBody 参数。
  • 函数式端点: 基于Lambda的、轻量级的、函数式的编程模型。你可以把它看成是一个小型的库或一组实用程序,应用程序可以用它来路由和处理请求。和注解式 controller 的最大区别是,应用程序从头到尾负责处理请求,而不是通过注解声明意图并被回调。

五、 适用性

Spring MVC 还是 WebFlux?

这是一个很自然的问题,但它却设置了一个不健全的二分法。实际上,两者都是为了扩大可用选项的范围而共同工作。两者的设计都是为了彼此的连续性和一致性,它们可以并排使用,每一方的反馈都对双方有利。下图显示了这两者的关系,它们的共同点,以及各自支持的独特之处:

我们建议你考虑以下具体要点:

  • 如果你有一个运行良好的Spring MVC应用程序,就没有必要改变。命令式编程是编写、理解和调试代码的最简单方式。你可以最大限度地选择库,因为从历史上看,大多数库都是阻塞的。
  • 如果你已经在“选购”非阻塞式 web stack,Spring WebFlux 提供了与该领域其他公司相同的执行模式优势,还提供了服务器(Netty、Tomcat、Jetty、Undertow和Servlet容器)的选择、编程模式(注解式 controller 和函数式 web 端点)的选择,以及响应式库(Reactor、RxJava或其他)的选择。
  • 如果你对用于Java 8 lambdas或Kotlin的轻量级功能性Web框架感兴趣,你可以使用Spring WebFlux 函数式 Web端点。对于需求不那么复杂的小型应用或微服务来说,这也是一个不错的选择,可以从更大的透明度和控制力中受益。
  • 在微服务架构中,你可以将应用与 Spring MVC 或 Spring WebFlux controller 或与 Spring WebFlux 函数式端点混合在一起。在这两个框架中支持相同的基于注解的编程模型,可以更容易地重复使用知识,同时也可以为正确的工作选择正确的工具。
  • 评估一个应用程序的简单方法是检查其依赖。如果你有阻塞的持久化API(JPA、JDBC)或网络API需要使用,Spring MVC 至少是普通架构的最佳选择。用 Reactor 和 RxJava 在单独的线程上执行阻塞调用在技术上是可行的,但你不会充分利用非阻塞的 Web stack。
  • 如果你有一个调用远程服务的 Spring MVC 应用,可以试试响应式 WebClient。你可以直接从Spring MVC Controller 方法中返回响应式类型(Reactor、RxJava或 其他)。每次调用的延迟越大或调用之间的相互依赖性越大,好处就越明显。Spring MVC Controller 也可以调用其他响应式组件。
  • 如果你有一个大的团队,请记住在转向非阻塞、函数式和声明式编程时的陡峭学习曲线。在没有完全转换的情况下,一个实用的方法是使用响应式的 WebClient。除此之外,从小处着手,并衡量其好处。我们预计,对于广泛的应用,这种转变是不必要的。如果你不确定要寻找什么好处,可以从了解非阻塞I/O的工作原理(例如,单线程Node.js的并发性)及其影响开始。

六、服务器

Spring WebFlux支持Tomcat、Jetty、Servlet容器,以及Netty和Undertow等非Servlet运行时。所有的服务器都适应于低级别的 通用API,这样就可以跨服务器支持更高级别的 编程模型。

Spring WebFlux没有内置的支持来启动或停止服务器。然而,通过Spring配置和 WebFlux基础架构 组装 一个应用程序,并通过几行代码来 运行它 是很容易的。

Spring Boot 有一个 WebFlux starter,可以自动完成这些步骤。默认情况下,starter 使用Netty,但通过改变Maven或Gradle的依赖,可以轻松切换到Tomcat、Jetty或Undertow。Spring Boot默认使用Netty,因为它在异步、非阻塞空间中使用得更广泛,可以让客户端和服务器共享资源。

Tomcat和Jetty都可以与Spring MVC和WebFlux一起使用。然而,请记住,它们的使用方式是非常不同的。Spring MVC依赖于Servlet阻塞式I/O,并让应用程序在需要时直接使用Servlet API。Spring WebFlux依赖于Servlet的非阻塞I/O,并在一个低级别的适配器后面使用Servlet API。它没有被暴露出来以供直接使用。

对于 Undertow,Spring WebFlux 直接使用 Undertow 的API,而不使用 Servlet API。

七、 性能

性能有很多特点和含义。响应式和非阻塞式通常不会使应用程序运行得更快。在某些情况下,它们可以(例如,如果使用 WebClient 来并行运行远程调用)。总的来说,用非阻塞的方式做事需要更多的工作,这可能会稍微增加所需的处理时间。

响应式和非阻塞式的关键预期好处是能够用少量的、固定的线程和较少的内存来扩展。这使得应用程序在负载下更有弹性,因为它们以一种更可预测的方式扩展。然而,为了观察这些好处,你需要有一些延迟(包括缓慢和不可预测的网络I/O的混合)。这就是 reactive stack 开始显示其优势的地方,其差异可能是巨大的。

八、 并发模型

Spring MVC和Spring WebFlux都支持注解 Controller ,但在并发模型以及阻塞和线程的默认假设方面有一个关键区别。

在Spring MVC(以及一般的servlet应用程序)中,假定应用程序可以阻塞当前线程,(例如,用于远程调用)。出于这个原因,servlet容器使用一个大的线程池来吸收请求处理过程中可能出现的阻塞。

在Spring WebFlux(以及一般的非阻塞服务器)中,假定应用程序不会阻塞。因此,非阻塞服务器使用一个小的、固定大小的线程池(event loop worke)来处理请求。

"扩展"和 "少量的线程" 听起来可能是矛盾的,但永远不阻塞当前线程(而依靠回调)意味着你不需要额外的线程,因为没有阻塞的调用需要吸收。

1、调用阻塞 API

如果你确实需要使用一个阻塞库呢?Reactor和RxJava都提供了 publishOn 操作符,可以在不同的线程上继续处理。这意味着有一个简单的救命稻草。然而,请记住,阻塞式API并不适合这种并发模型。

2、可变的状态

在Reactor和RxJava中,你通过操作符声明逻辑。在运行时,会形成一个响应式 pipeline,在这个 pipeline 中,数据被按顺序、分阶段地处理。这样做的一个主要好处是,它使应用程序不必保护易变的状态,因为该 pipeline 中的应用程序代码不会被并发调用。

3、线程模型

在使用Spring WebFlux运行的服务器上,你应该看到哪些线程?

  • 在 "vanilla" Spring WebFlux 服务器上(例如,没有数据访问,也没有其他可选依赖),你可以期待服务器有一个线程,其他几个线程用于请求处理(通常与CPU核的数量一样多)。然而,Servlet容器开始时可能有更多的线程(例如Tomcat上的10个),以支持Servlet(阻塞)I/O和Servlet 3.1(非阻塞)I/O的使用。
  • 响应式 WebClient 以事件循环(event loop)方式运行。所以你可以看到与之相关的少量固定的处理线程(例如,reactor-http-nio- 与 Reactor Netty connector)。然而,如果 Reactor Netty 同时用于客户端和服务器,两者默认共享事件循环资源。
  • Reactor 和 RxJava 提供了线程池抽象,称为调度器(scheduler),与用于将处理切换到不同线程池的 publishOn 操作符一起使用。调度器的名字暗示了特定的并发策略—​例如,“parallel”(用于有限数量线程的CPU绑定工作)或 “elastic”(用于有大量线程的I/O绑定工作)。如果你看到这样的线程,这意味着一些代码正在使用特定的线程池 Scheduler 策略。
  • 数据访问库和其他第三方依赖也可以创建和使用它们自己的线程。

4、配置

Spring框架不提供对启动和停止 服务器 的支持。要配置服务器的线程模型,你需要使用服务器特定的配置API,或者,如果你使用Spring Boot,请检查每个服务器的Spring Boot配置选项。你可以直接 配置 WebClient。对于所有其他的库,请看它们各自的文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1008583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Protege的知识建模实战

一.Protege简介、用途和特点 1.Protege简介 Protege是斯坦福大学医学院生物信息研究中心基于Java开发的本体编辑和本体开发工具,也是基于知识的编辑器,属于开放源代码软件。这个软件主要用于语义网中本体的构建,是语义网中本体构建的核心开发…

山西电力市场日前价格预测【2023-09-14】

日前价格预测 预测说明: 如上图所示,预测明日(2023-09-14)山西电力市场全天平均日前电价为314.65元/MWh。其中,最高日前电价为362.07元/MWh,预计出现在19: 00。最低日前电价为154.13元/MWh,预计…

c、c++、java、python、js对比【面向对象、过程;解释、编译语言;封装、继承、多态】

C 手动内存管理:C语言没有内置的安全检查机制,容易出现内存泄漏、缓冲区溢出等安全问题。 适用于系统级编程 C 手动内存管理:C需要程序员手动管理内存,包括分配和释放内存,这可能导致内存泄漏和指针错误。 适用于…

一百七十五、Kettle——海豚调度kettle任务的脚本需不需要配置log日志文件?

一、目的 总结一下,在用海豚调度kettle任务脚本是需不需要配置log日志文件? 二、两种情形介绍 (一)海豚配置kettle任务调度脚本时加log日志文件 #!/bin/bash source /etc/profile /usr/local/hurys/dc_env/kettle/data-integ…

《PostgreSQL物化视图:创建、维护与应用》

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: &#x1f4da…

Python——urllib库

urllib是一个用来处理网络请求的python内置库。 一.基本使用 二.一个类型和6个方法 2.1 一个类型 urllib的request库中urlopen方法返回的类型&#xff1a;<class http.client.HTTPResponse>。为了与之后request库做区别。 2.2 6个方法 read()方法&#xff1a;获得响应…

spark6. 如何设置spark 日志

spark yarn日志全解 一.前言二.开启日志聚合是什么样的2.1 开启日志聚合MapReduce history server2.2 如何开启Spark history server 三.不开启日志聚合是什么样的四.正确使用log4j.properties 一.前言 本文只讲解再yarn 模式下的日志配置。 二.开启日志聚合是什么样的 在ya…

C#,数值计算——哈希表的实现代码

1 文本格式 using System; using System.Collections; using System.Collections.Generic; namespace Legalsoft.Truffer { public class Hash<K, V> : Hashtable<K> { private List<V> els { get; set; } new List<V>(); public Ha…

iPhone苹果15手机圆点怎么设置让屏幕上显示出来圆形图标?

iPhone苹果15手机圆点怎么设置让屏幕上显示出来圆形图标&#xff1f; 1、在iPhone苹果手机上找到「设置」并点击打开&#xff1b; 2、在苹果iPhone设置内找到「辅助功能」并点击打开&#xff1b; 3、在苹果iPhone手机辅助功能内的动作交互内找到「触控」并点击打开&#xff1b…

车联网远程监控管理提升车辆调度效率,实现高效运营

随着智慧城市建设与物联网技术发展&#xff0c;车辆使用4G工业路由器网络实现车联网&#xff0c;并对车上视频监控、GPS定位以及温湿度传感器等信息进行数据采集和实时传输。这些数据的采集和监测将通过4G网络上传到管理平台&#xff0c;为车辆调度和运行效率的优化提供了有力的…

全局滚动条样式修改,elementUI table底部滚动条遮挡

/* 整个滚动条 */ ::-webkit-scrollbar {width: 15px !important;height: 15px !important; } /* 滚动条上的滚动滑块 */ ::-webkit-scrollbar-thumb {border-radius: 32px; } /* 滚动条轨道 */ ::-webkit-scrollbar-track {border-radius: 32px; }// 如果想作用组件 可以 .xxx…

项目--苍穹外卖

1.| constant | 存放相关常量类 | | context | 存放上下文类 | | enumeration | 项目的枚举类存储 | | exception | 存放自定义异常类 | | json | 处理json转换的类 | | properties | 存放SpringBoot相关的配置属性类 | | result | 返回结果类的封装 | | utils | 常用工具类 | …

✔ ★算法基础笔记(Acwing)(二)—— 数据结构(17道题)【java版本】

数据结构 1. 单链表模板1. 单链表(7分钟) 2. 双链表模板1. 双链表 3. 模拟栈1. 模拟栈(一个数组即可)2. 表达式求值(20分钟) 4. 队列 tt -1,hh 0;1. 模拟队列 5. 单调栈1. 单调栈(4分钟)3.14 6. 单调队列1. 滑动窗口例题(10分钟) 7. KMP1. KMP字符串(10分钟)二刷体会★三刷体…

Python之glob

import os import glob import datetime # for dirpath, dirname, files in os.walk(rE:\htdos\Project): # print(dirpath, dirname, files)# glob.glob(**) 当前目录的所有文件和文件夹 # glob.glob(**/) 当前目录的所有文件夹 # glob.glob(20*) 找到当前目录以20开头的…

HDMI协议Ver2.0a(学习笔记)

1 简介 本规范由HDMI论坛制定 2.目的和范围 本文件构成了高清多媒体接口2.0版规范&#xff08;HDMI规范2.0版&#xff09;。本规范通过引用纳入了HDMI规范1.4b版&#xff0c;并定义了附加和改进的功能。对Source、Sink、中继器和电缆的合规性所需的机械、电气、行为和协议要…

解决java.lang.reflect.ReflectionException: Reflection error

解决java.lang.reflect.ReflectionException: Reflection error 摘要引言正文1. 理解异常的根本原因2. 处理反射操作错误用法获取类的方式不正确&#xff1a;调用方法的方式不正确&#xff1a; 3. 处理访问权限问题4. 异常处理 总结参考资料 博主 默语带您 Go to New World. ✍…

Unity 课时 1 : No.1 模拟面试题

课时 1 : No.1 模拟面试题 C# 1.装箱和拆箱是什么&#xff1f; ​ 对于应用类型。将基类的子类转化成基类称为装箱。将基类转换成子类&#xff0c;成为拆箱。装箱通常使用隐式转换&#xff0c;拆箱可以使用 as 或者 强制转换。同样对于值类型也可以进行装箱和拆箱。 答案&a…

C++11—线程库

C线程库 文章目录 C线程库线程的创建thread提供的成员函数get_idthis_thread命名空间线程的回收策略join detach 线程函数参数mutex的种类lock_guardunique_lock原子性操作库(atomic)cas操作windows和Linux创建进程的区别 线程的创建 调用无参的构造函数 thread() noexcept;#in…

视频云存储/安防监控/AI分析/视频AI智能分析网关行为分析算法应用场景汇总

随着城市经济的日益发展&#xff0c;城市建设也如火如荼。很多施工人员为了自身方便&#xff0c;无视安全规章制度&#xff0c;不按照安全施工要求进行着装、施工。具有很大的安全隐患&#xff0c;一旦发生事故&#xff0c;就会造成人员损伤和工程进度的挤压。为了改善此问题&a…

QT实战之翻金币游戏【详细过程及介绍】

目录 前言 一、游戏整体分析 二、创建项目 三、添加资源 四、主界面实现 1、设置游戏主场景基本配置 2、设置背景图片 3、创建开始按钮并设置动画 4、创建关卡选择界面并实现主界面与其的切换 五、关卡选择界面实现 1、设置关卡选择场景基本配置 2、设置关卡选择场…