一、反应式编程初探
什么是反应式编程
有这么一个场景:
假设你定了一年的费用,并支付了一整年的订阅费用。可是过了几天,你却没有收到一份报纸,打电话给报社问为什么没有收到报纸。他告诉你:“你支付的是一年的费用,现在一年还没结束,当这一年结束的时候,我们会把这一年的报纸一次性交付给你。”这听起来很不合理,报纸是一种很具有时效性的东西,昨天的报纸对于今天来说已经没有多大意义了,可况年底将一年的报纸打包发给你,正常情况下应该是将每天都给你报纸。
好在这只是一个假设的场景,并不是真实的。从上面这个假设的场景中,我们感觉到了不合理。
再来对比一下命令式编程和反应式编程
命令式编程:非常类似于上文中虚拟的订阅报纸的方法。我们普通的编程方法几乎都是命令式编程,按顺序执行一批代码,下一个任务的执行依赖于上一个任务的成功执行,最后等到所有代码执行完毕,我们才能拿到最终的输出结果,这就好像是必须等一年才能拿到今年的报纸。
反应式编程:类似于生活中真实的订阅报纸。虽然我订阅的是一年份的报纸,但是我每天都可以收到新的报纸。我们不需要等到所有代码都执行完毕才能取到数据,任务可以是并行处理的,我们可以得到中间的数据,每个任务处理一部分数据,最后进行汇总。
这么说可能还是有点绕,可以想想一个这么场景。
我们可以选择这两种方式打水仗。
-
一种是用一个个气球装满水,然后直接往对方身上扔,水球扔到对方身上,水一下子迸发而出。这就好像是命令式编程。
-
另外一种则是使用一根水管,连续不断的将水喷到对方身上。这就好像是反应式编程。
为什么需要反应式编程?
反应式编程旨在提供无阻塞异步回压的异步处理。
举个例子:什么时候需要所谓的无阻塞异步处理呢?
比如HTTP服务器,对于这些服务器来说,它肯定服务的不只是一个人,它要服务很多人。如果按照同步的方法,那么服务器一次就要被一个客户端独占了。而在我们通常的使用场景中,一个客户端并不是时时刻刻都在向服务器发送请求,比如我们浏览网页的时候,我们点进去一个页面,get到服务器端返回来的页面以后,我们起码也要在这个页面上浏览好几秒钟才会再次向服务器发送请求跳转到下一个页面吧。如果服务器被一个客户端独占了,自然就没有办法响应其它客户端的请求。那肯定不行。怎么办呢?这时候就采用异步的方法。
所谓的同步和异步就好像是打电话和发邮件的区别。打电话得保证双方都得同时占线,而且双方打电话的时候就不能干其它事情了,如果电话那头话说得又非常慢,你还得耐着性子等着他说完。发邮件则不是,你给对方发邮件,对方空闲下来就会去看看邮箱,没有空的时候他就会忙他的事情。你也是,发完邮件以后你就去干自己的事情,不用像打电话一样必须等着对方说完才会去做自己的事情,这样能极大的提高对时间的利用效率。
这里也是,若HTTP服务器采用异步的方法,服务器就不会被一个客户端独占了。你向服务器发送请求,服务器不会马上响应你,而是会等到它忙忘手中的事情才回来理你。这样的话服务器不会马上响应你的需求,影响大吗?得看你的实际需求,如果你是在浏览网页,可能基本上没什么影响,因为你并不是时时刻刻都会向服务器发起请求,而是隔三岔五的请求一下。而对于服务器来说,这样它就能同时服务很多客户端了,而不会被一个客户端长时间占线。
反应式编程的规范
反应式编程的规范可以总结为4个接口Publisher,Subscriber,Subscription,Processor
Publisher负责生成数据,并将数据发送给Subscriber。一个Subscriber对应着一个Subscription。Subscriber可以使用Subscription来管理其订阅情况。
-
首先是Subscriber订阅Publisher。订阅成功以后,Subscriber就会得到一个Subscription对象。Subscription中有两个方法:request方法和cancel方法。
-
Subscriber得到一个Subscription对象以后,就可以调用该Subscription对象的request方法,请求Publisher发送数据给Subscriber。通过调用cancel方法就可以取消对Publisher的订阅。
-
Processor则是用来处理Publisher发送给Subscriber的数据。
对应的关系如图所示:

二、上手反应式编程(使用Spring中的Reactor)
对比反应式编程和命令式编程代码
先来看看两段代码。第一段是命令式编程。第二段则是Spring中的反应式编程框架Reactor来实现的。
这两段代码都能完成相同的功能
String name = "Simon";
String capitalName = name.toUpperCase();
String greeting = "Hello " + capitalName + "!";
System.out.println(greeting);
Mono.just("Simon")
.map(n -> n.toUpperCase())
.map(cn -> "Hello " + cn + "!")
.subscribe(gn -> System.out.println(gn));
添加相应依赖
正式使用Reactor之前需要添加相应依赖。
如果使用Spring Boot,那么则需要添加如下依赖
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
Reactor还提供了test功能,需要的话则可以添加如下依赖:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
添加了测试功能以后就可以使用StepVerifier进行测试
Flux<String> fruitFlux = Flux
.just("Apple", "Orange", "Grape", "Banana", "Strawberry");
StepVerifier.create(fruitFlux)
.expectNext("Apple")
.expectNext("Orange")
.expectNext("Grape")
.expectNext("Banana")
.expectNext("Strawberry")
.verifyComplete();
Mono和Flux
Reactor有两种核心类型:一种是Mono,一种是Flux。Mono的英文意思是单声道。对比如下两种图可知,Mono处理的数据项不超过一个,也就是所谓的单。而Flux则可以处理一个或者是多个数据项。
Mono示意图

Flux示意图

三、反应式编程的常见操作
1.创建操作
在使用反应式编程之前,我们得先创建反应式类型的数据,也就是说得先将数据封装为Flux或者是Mono类型的对象。
根据对象创建
Flux<String> fruitFlux = Flux
.just("Apple", "Orange", "Grape", "Banana", "Strawberry");
/*
创建好放映室类型的对象以后,就可以开始反应式操作了。
需要注意的是反应式类型的数据离不开subscribe
只有被订阅了整个数据才会开始流动起来,操作才会一步一步往下走
*/
fruitFlux.subscribe(f -> System.out.println(f));
根据数组创建
String[] fruits = new String[] {
"Apple", "Orange", "Grape", "Banana", "Strawberry" };
Flux<String> fruitFlux = Flux.fromArray(fruits);
根据List创建
List<String> fruitList = new ArrayList<>();
fruitList.add("Apple");
fruitList.add("Orange");
fruitList.add("Grape");
fruitList.add("Banana");
fruitList.add("Strawberry");
Flux<String> fruitFlux = Flux.fromIterable(fruitList

本文深入探讨反应式编程,通过Spring中的Reactor框架,对比命令式编程,阐述反应式编程的优势。内容涵盖反应式编程的规范、使用Mono和Flux操作数据、数据创建、组合、转换和过滤,以及逻辑操作。通过实例展示了如何在反应式流上缓存数据,帮助读者理解并掌握反应式编程的核心概念。
,反应式编程和命令式编程的区别。如何使用Spring中的Reactor。Reactor中常用的操作。Mono和Flux。&spm=1001.2101.3001.5002&articleId=111235576&d=1&t=3&u=0059818bad2146e497b172695c584371)
2489

被折叠的 条评论
为什么被折叠?



