一、反应式编程
反应式编程是一种可以替代命令式编程的编程范式。这种可替代性存在的原因在反应式编程解决了命令式编程中的一些限制。理解这些限制,有助于你更好地理解反内式编程模型的优点。
命令式的理念:依次地按照顺序执行所编写的代码,在某项任务开始执行之前,程序需要等待之前的任务完成。同时在整个处理过程中的每一步, 要处理的每一步数据都必须是完全可用的,以便将它们作为一个整体进行处理。
一开始一切都很美好,直到我们遇到问题。在执行某项任务的时候,特别是I0任务(将数据写人DB或者从远程服务器获取数据),触发这项任务的线程实际上是被阻塞的,在任务完成之前它不能做任何事情。坦白来说,阻塞线程是一种浪费。
相比之下,反应式编程本质上是函数式的和声明式的。相对于描述一组将依次执行的步骤,反应式编程描述了数据将会流经的管道或者流。相对于要求将被处理的数据作个整体进行处理, 反应式流可以在数据可用时立即开始处理。实际上,传人的数据为具无限的(比如,一个某个地理位置的实时温度测量数据的恒定流)。
拿现实世界类比一下可以将命令式编程看作是水气球,而将反应式编程看作是花园里的软管。在夏天,这两者都是偷袭和愉悦毫无戒心的朋友的好方式,但是它们的运作方式却不同。
■水气球只能一次性地填满有效载荷, 并在撞到目标时弄湿对象。水气球的容量有限,如果你想要弄湿更多人(或者把同一个人弄得更加湿透一点),那么唯一的选择就是增加水气球的数量。
■花园软管的有效载荷是从水龙头到喷嘴的水流。在特定的时间点,花园软管的容量可能是有限的,但是在打水仗的过程中它的容量却是无限的。只要水源源不断地从龙头流入软管中,水就会源源不断地从喷嘴喷出去。同一个软管非常好扩展,你可以尽情地和更多的朋友打水仗。
虽然水气球(或者命令式编程)没有什么固有的问题,但是持有软管(或者能够应用反应式编程)的人通常在伸缩性和性能方面更具优势。
在经历了一些反应式编程实践的基础上产生了后来的反应式流(Reactive Streams)规范。该规范定义了反应式流的相关接口,并集成到了java 9中。
反应式流
反应式流)( Reactive Streams) 是由Netflix 、Lightbend 和Pivotal ( Spring背后的公司)的工程师于2013年年底开始制定的一种规范。反应式流旨在提供无阻塞回压的异步流处理标准。
我们已经触及了反应式编程的异步特性,它使我们能够并行执行任务,从而实现更高的可伸缩性。通过回压,数据消费者可以限制它们想要处理的数据数量,避免被过快的数据源所淹没。
Java的流和反应式流
Java的流和反应式流之间有很多相似之处。首先,它们的名字中都有流(Stream)这个词。它们还提供了用于处理数据的函数式API。事实上, 正如你稍后将会在我们介绍Reactor时看到的那样,它们甚至可以共享许多相同的操作。
Java的流通常都是同步的,并且只能处理有限的数据集从本质上来说,它们只是使用函数来对集合进行迭代的一种方式。
反应式流支持异步处理任意大小的数据案,同样也包括无限数据集。只要数据就绪,它们就能实时地处理数据,并且能够通过回压来避免压垮数据的消费者。
初识Reactor
Reactor完全基于反应式流规范设计和实现的库。Reactor也是Spring 5中反应式编程的基础。学习和掌握Reactor可以更好低理解Spring 5中的相关概念。
Flux和Mono是Reactor中的两个基本概念。Flux表示的是包含0到N个元素的异步序列。在该序列中可以包含三种不同类型的消息通知:正常的包含元素的消息、序列结束的消息和序列出错的消息。当消息通知产生时,订阅者中对应的方法onNext(),onComplete()和onError()会被调用。Mono表示的是包含0或者1个元素的异步序列。该序列中同样可以包含于flux相同的三种类型的消息通知。Flux和Mono之间可以进行转换。对一个Flux序列进行计数操作,得到的结果是一个Mono对象。把两个Mono序列合并到一起,得到的是一个Flux对象。
反应式编程小结
反应式编程范式为开发高性能web应用带来了新的机会和挑战。对于复杂的应用来说,反应式编程和回压的优势会体现出来,可以带来整体的性能的提升。Spring5引人了一个非阻塞异步的Web框架,该框架在很大程度上是基于Reactor项目的,能够解决Web应用和API中对更好的可扩展性的需求,接下来我们看-下SpringWebFlux:面向Spring的反应式Web框架。
二、WebFlux简介
传统的基于Servlet的Web框架,如Spring MVC,在本质上都是阻塞和多线程的,每个连接都会使用一个线程。在请求处理的时候,会在线程池中拉取一个工作者( worker )线程来对请求进行处理。同时,请求线程是阻塞的,直到工作者线程提示它已经完成为止。
这样带来的后果就是阻塞式Web框架在大量请求下无法有效地扩展。缓慢的工作者线程所带来的延迟会使情况变得更糟,因为它将花费更长的时间才能将工作者线程送回池中,准备处理另一个请求。在某些场景中,这种设计完全可以接受。事实上,在很大程度上这就是十多年来大多数Web应用程序的开发方式,但是时代在改变。
这些Web应用程序的客户端以前是偶尔浏览网站的人们,而现在这些人会频繁消费内容而且会使用与HTTP API协作的应用程序。随着消费Web应用的客户端越来越多,可扩展性比以往任何时候都更加重要。
异步的Web框架能够以更少的线程获得更高的可扩展性,通常它们只需要与CPU核心数量相同的线程。通过时间轮询机制这些框架能够用一个线程处理很多请求,这样每次连接的成本会更低。
时间轮询机制
在事件轮询中,所有事情都是以事件的方式来进行处理的,(包括请求以及密集型操作(如数据库和网络操作)的回调。当需要执行成本高昂的操作时,(事件轮询会为该操作注册一个回调, 这样操作可以并行执行)而事件轮询则会继续处理其他的事件。当操作完成时,事件轮询机制会将其作为一个事件,这一点与请求是相同的。这样达到的效果就是,在面临大量负载的时候,异步Web框架能够以更少的线程实现更好的可扩展性,这样会减少线程管理的开销。
Spring 5中WebFlux 模块的名称是 spring-webflux,名称中的 Flux 来源于 Reactor 库中的反应式类型 Flux。在服务器端,WebFlux 支持两种不同的编程模型:第一种是基于Spring MVC 中使用的Java 注解的方式;第二种是基于 Java 8 的 lambda 表达式的函数式编程模型。这两种编程模型只是在代码编写方式上存在不同。它们运行在同样的反应式底层架构之上,因此在运行时是相同的。
WebFlux 需要底层提供运行时的支持,WebFlux 可以运行在支持 Servlet 3.1 非阻塞 IO API 的 Servlet 容器上,或是其他异步运行时环境,如 Netty 和 Undertow。
三、webFlux应用场景
特别适合应用在IO密集型服务中,比如写文件的日志收集、微服务网关这样的应用中。(IO密集型包括:磁盘IO密集型,网络IO密集型,使用异步非阻塞式编程模型,能够显著地提升吞吐量。并且官方的推荐使用Netty跑WebFlux)
如果你的代码中有任何阻塞操作,请谨慎选择WebFlux。
WebFlux并不保证应用能运行的更快,但是它主打的是横向扩展和低内存消耗,它的性能需要在一些特定的场景才能展现,比如慢网络IO的场景。
四、Spring MVC VS Spring WebFlux
首先需要指出webflux不是spring mvc的替代方案!webflux强调的是异步非阻塞,spring mvc强调的是同步阻塞,如果方案大部分偏向于非同步,则spring webflux才是首选。另外,如果依赖了大量阻塞式持久化 API 和网络 API,建议使⽤ Spring MVC。
WebMVC:springMvc构建在servlet api之上,因此需要Servlet容器(比如Tomcat)才能执行。使用的是同步阻塞式IO模型,每一个请求对应一个线程去处理。
WebFlux:spring webflux是一个异步非阻塞式的web框架,它能够充分利用多核cpu的硬件资源去处理大量的并发请求。
相同之处
都可以使用springmvc注解,如@Controller,方便我们在两个web框架中自由转换;
都可以使用tomcat,jetty,undertow, servlet容器(servlet 3.1+)
注意点:
spring mvc因为是使用的同步阻塞式,更方便开发人员编写功能,debug测试等,一般来说,如果spring mvc能够满足的场景,就尽量不要用webflux;
使用WebFlux有一个很有 意思的副作用, 即WebFlux 的默认嵌人式服务器是Netty而不是Tomcat。Netty 是一个异步 、事件驱动的服务器,非常适合Spring WebFlux这样的反应式Web框架。
Spring WebFlux的控制器方法要接受和返回反应式类型,如Mono和Flux,而不是领域类型和集合。
webflux目前不支持mysql;
来源:Spring 实战(5)
码弄笔记 https://vlambda.com/wz_wCellgFzo0.html