【Spring-WebFlux】响应式
Spring Web vs Spring Webflux: https://medium.com/@burakkocakeu/spring-web-vs-spring-webflux-9224260c47b5
下面2个内容存在部分重合
响应式圣经:10W字,实现Spring响应式编程自由 https://www.cnblogs.com/crazymakercircle/p/17119024.html#autoid-h3-25-0-0
Flux 和 Mono 、reactor实战 https://www.cnblogs.com/crazymakercircle/p/16127013.html
响应式编程
基本知识
为了应对高并发服务器端开发场景,在2009 年,微软提出了一个更优雅地实现异步编程的方式——Reactive Programming,我们称之为响应式编程。
Netflix 和LightBend 公司提供了RxJava 和Akka Stream 等技术,使得Java 平台也有了能够实现响应式编程的框架。
在2017 年9 月28 日,Spring 5 正式发布。Spring 5 发布最大的意义在于,它将响应式编程技术的普及向前推进了一大步。而同时,作为在背后支持Spring 5 响应式编程的框架Spring Reactor,也进入了里程碑式的3.1.0 版本。
规范 Reactive Stream,实现的框架有 RxJava和Project Reactor。Spring WebFlux基于Project Reactor。
与“响应式”相关的另一个重要机制是非阻塞反压( non-blocking back pressure.)。
- 在同步命令式代码中,阻塞调用是一种天然的反压形式,它强制调用者等待。
- 在非阻塞代码中,控制事件的发生速率就显得尤为重要,这样才能避免快速生成的事件导致目标服务器不堪重负。
响应式流(Reactive Streams)是一个 小型规范 (Java 9 也采用了该规范),它定义了异步组件之间具有反压机制的交互。例如,数据存储库(充当 发布者)可以生成数据,然后 HTTP 服务器(充当 订阅者)可以将这些数据写入响应。响应式流的主要目的是让订阅者控制发布者生成数据的速度。
异步调用 +IO Reactor 事件驱动,可以避免将 CPU 浪费在等待网络 IO 和磁盘 IO 时上,实现提高资源使用率。
Spring Reactor 一般提供两种响应式 API :
Mono:实现发布者,并返回 0 或 1 个元素Flux:实现发布者,并返回 N 个元素
Spring WebFlux
WebFlux 的核心依赖项是 Reactor,但它可以通过 Reactive Streams 与其他响应式库互操作。通常,WebFlux API 接受一个普通Publisher 的输入,在内部将其转换为 Reactor 类型,使用该类型,并返回一个 Flux或 Mono作为输出。因此,您可以传递任何类型Publisher作为输入,并对其输出应用操作,但您需要对输出进行适配才能与其他响应式库一起使用。在可行的情况下(例如,使用注解控制器),WebFlux 可以透明地适配 RxJava 或其他响应式库。
编程模型:
Spring WebFlux 提供了两种编程模型供选择
-
注解控制器:与 Spring MVC 一致,并基于
spring-web模块中的相同注解。Spring MVC 和 WebFlux 控制器都支持响应式(Reactor 和 RxJava)返回类型,因此很难区分它们。一个显著的区别是 WebFlux 还支持响应式参数@RequestBody。 -
函数式端点:基于 Lambda 表达式的轻量级函数式编程模型。您可以将其视为一个小型库或一组实用程序,应用程序可以使用它们来路由和处理请求。与注解控制器的主要区别在于,应用程序负责从头到尾处理请求,而不是通过注解声明意图并等待回调。
Spring MVC 还是 WebFlux? https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html
-
如果你的 Spring MVC 应用运行良好,则无需更改。命令式编程是编写、理解和调试代码最简单的方式。由于大多数库历来都是阻塞式的,因此你可以选择最多的库。
-
如果您正在寻找非阻塞式 Web 技术栈,Spring WebFlux 提供了与该领域其他技术栈相同的执行模型优势,并且还提供了多种服务器选择(Netty、Tomcat、Jetty、Undertow 和 Servlet 容器)、多种编程模型选择(带注解的控制器和函数式 Web 端点)以及多种响应式库选择(Reactor、RxJava 或其他)。
-
如果您正在寻找一款轻量级、函数式的 Web 框架,并希望将其与 Java 8 Lambda 表达式或 Kotlin 结合使用,那么 Spring WebFlux 函数式 Web 端点是一个不错的选择。对于需求较为简单的、规模较小的应用程序或微服务而言,它也是一个理想的选择,能够带来更高的透明度和控制力。
-
在微服务架构中,您可以混合使用 Spring MVC 或 Spring WebFlux 控制器,或者使用 Spring WebFlux 函数式端点。两种框架都支持相同的基于注解的编程模型,这使得知识复用更加便捷,同时也能确保针对不同任务选择合适的工具。
-
评估应用程序的一个简单方法是检查其依赖项。如果您需要使用阻塞式持久化 API(例如 JPA、JDBC)或网络 API,那么至少对于常见的架构而言,Spring MVC 是最佳选择。虽然 Reactor 和 RxJava 都允许在单独的线程上执行阻塞调用,但这无法充分利用非阻塞式 Web 技术栈的优势。
-
如果你的 Spring MVC 应用调用了远程服务,不妨试试响应式编程
WebClient。你可以直接从 Spring MVC 控制器方法返回响应式类型(Reactor、RxJava或其他)。每次调用的延迟越高,或者调用之间的依赖关系越紧密,响应式编程的优势就越明显。Spring MVC 控制器也可以调用其他响应式组件。 -
如果你的团队规模较大,请记住,转向非阻塞、函数式和声明式编程的学习曲线非常陡峭。一个无需完全切换的实用方法是先使用响应式编程
WebClient。除此之外,建议从小规模开始,并评估其带来的收益。我们预计,对于大多数应用场景而言,这种转变是不必要的。如果你不确定应该关注哪些收益,可以先了解非阻塞 I/O 的工作原理(例如,单线程 Node.js 中的并发)及其影响。
支持的服务器:
Spring WebFlux 支持 Tomcat、Jetty 和 Servlet 容器,以及 Netty 和 Undertow 等非 Servlet 运行时环境。
Spring Boot 提供了一个 WebFlux starter,可以自动执行这些步骤。默认情况下,该 starter 使用 Netty,但只需更改 Maven 或 Gradle 依赖项,即可轻松切换到 Tomcat、Jetty 或 Undertow。Spring Boot 默认使用 Netty,是因为它在异步、非阻塞领域应用更为广泛,并且允许客户端和服务器共享资源。
在Spring MVC(以及一般的 Servlet 应用程序)中,应用程序可能会阻塞当前线程(例如,进行远程调用)。因此,Servlet 容器使用大型线程池来应对请求处理期间可能出现的阻塞。
Spring WebFlux(以及一般的非阻塞服务器)中,假定应用程序不会阻塞。因此,非阻塞服务器使用小型、固定大小的线程池(事件循环工作线程)来处理请求。
线程模型
在运行 Spring WebFlux 的服务器上,您应该会看到哪些线程?
-
在一个“纯净”的 Spring WebFlux 服务器上(例如,没有数据访问或其他可选依赖项),服务器端会使用一个线程,而请求处理则使用多个线程(通常与 CPU 核心数相同)。然而,Servlet 容器可能会使用更多线程(例如,Tomcat 上可能使用 10 个线程),以同时支持 Servlet(阻塞式)I/O 和 Servlet 3.1(非阻塞式)I/O。
-
响应式
WebClient架构以事件循环的方式运行。因此,您可以看到与之相关的处理线程数量很少且固定(例如,reactor-http-nio-使用 Reactor Netty 连接器时)。但是,如果 Reactor Netty 同时用于客户端和服务端,则默认情况下两者共享事件循环资源。 -
Reactor 和 RxJava 提供了称为调度器的线程池抽象,用于与切换
publishOn操作符配合使用,以便将处理任务切换到不同的线程池。调度器的名称暗示了特定的并发策略——例如,“parallel”(用于 CPU 密集型任务,线程数量有限)或“elastic”(用于 I/O 密集型任务,线程数量众多)。如果您看到此类线程,则表示某些代码正在使用特定的线程池Scheduler策略。 -
数据访问库和其他第三方依赖项也可以创建和使用它们自己的线程。
编码实战
1、WebClient配置成单例使用
如果每次请求都创建 WebClient 实例导致连接池无法复用,资源浪费严重,性能急剧下降
1)线程安全:WebClient默认使用连接池(ReactorClientHttpConnector连接池)。单例模式下,所有请求共享同一个连接池,避免重复创建和销毁连接,减少资源开销
2)资源高效
-
- 连接池复用:WebClient 默认使用连接池(如
ReactorClientHttpConnector的连接池)。单例模式下,所有请求共享同一个连接池,避免重复创建和销毁连接,减少资源开销。 - 避免内存浪费:频繁创建 WebClient 实例会导致每个实例都维护独立的连接池、拦截器链等,增加内存占用。
- 连接池复用:WebClient 默认使用连接池(如
3)配置一致性:单例模式确保所有请求共享相同的底层配置(如超时、认证、拦截器等),避免因多实例配置不一致导致的潜在问题。
4)性能优化:单例模式下,连接池可以更高效地管理 TCP 连接(如保持长连接、复用空闲连接),降低延迟,提升高并发场景下的吞吐量。
特殊场景:需要配置多实例场景
1)不同配置需求:需要为不同的服务或接口配置不同的超时、连接池参数或认证方式,可以创建多个 WebClient 实例
2)隔离故障场景:如果某些服务需要独立的连接池或错误处理逻辑(如隔离慢服务的影响),可以单独配置实例。
浙公网安备 33010602011771号