简介

Reactor是reactivex家族的一个非常重要的成员,Reactor是第四代的reactive library,它是基于Reactive Streams标准基础上开发的,主要用来构建JVM环境下的非阻塞应用程序。

今天给大家介绍一下Reactor。

Reactor简介

Reactor是基于JVM的非阻塞API,他直接跟JDK8中的API相结合,比如:CompletableFuture,Stream和Duration等。

它提供了两个非常有用的异步序列API:Flux和Mono,并且实现了Reactive Streams的标准。

并且还可以和reactor-netty相结合,作为一些异步框架的底层服务,比如我们非常熟悉的Spring MVC 5中引入的WebFlux。

我们知道WebFlux的底层使用的是reactor-netty,而reactor-netty又引用了Reactor。所以,如果你在POM中引入了webFlux依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-webflux</artifactId>
  4. </dependency>

那么项目将会自动引入Reactor。

如果你用的不是Spring webflux,没关系,你可以直接添加下面的依赖来使用Reactor:

  1. <dependency>
  2. <groupId>io.projectreactor</groupId>
  3. <artifactId>reactor-core</artifactId>
  4. </dependency>

reactive programming的发展史

最最开始的时候微软为.NET平台创建了Reactive Extensions (Rx) library。接着RxJava实现了JVM平台的Reactive。

然后Reactive Streams标准出现了,它定义了Java平台必须满足的的一些规范。并且已经集成到JDK9中的java.util.concurrent类中。

在Flow中定义了实现Reactive Streams的四个非常重要的组件,分别是Publisher,Subscriber,Subscription和Processor。

Iterable-Iterator 和Publisher-Subscriber的区别

一般来说reactive在面向对象的编程语言中是以观察者模式的扩展来使用的。

我们来具体看一下这个观察者模式的实现,以Publisher和Subscriber为例:

  1. public static interface Publisher<T> {
  2. public void subscribe(Subscriber<? super T> subscriber);
  3. }
  1. public static interface Subscriber<T> {
  2. public void onSubscribe(Subscription subscription);
  3. public void onNext(T item);
  4. public void onError(Throwable throwable);
  5. public void onComplete();
  6. }

上面定义了两个接口,Publisher和Subscriber,Publisher的作用就是subscribe到subscriber。

而subscriber定义了4个on方法,用来触发特定的事件。

那么Publisher中的subscribe是怎么触发Subscriber的onSubscribe事件呢?

很简单,我们看一个具体的实现:

  1. public void subscribe(Flow.Subscriber<? super T> subscriber) {
  2. Subscription sub;
  3. if (throwable != null) {
  4. assert iterable == null : "non-null iterable: " + iterable;
  5. sub = new Subscription(subscriber, null, throwable);
  6. } else {
  7. assert throwable == null : "non-null exception: " + throwable;
  8. sub = new Subscription(subscriber, iterable.iterator(), null);
  9. }
  10. subscriber.onSubscribe(sub);
  11. if (throwable != null) {
  12. sub.pullScheduler.runOrSchedule();
  13. }
  14. }

上面的例子是PullPublisher的subscribe实现。我们可以看到,在这个subscribe中触发了subscriber.onSubscribe方法。而这就是观察者模式的秘密。

或者说,当Publisher调用subscribe的时候,是主动push subscriber的onSubscribe方法。

熟悉Iterable-Iterator模式的朋友应该都知道,Iterator模式,其实是一个主动的pull模式,因为需要不断的去调用next()方法。所以它的控制权是在调用方。

为什么要使用异步reactive

在现代应用程序中,随着用户量的增多,程序员需要考虑怎么才能提升系统的处理能力。

传统的block IO的方式,因为需要占用大量的资源,所以是不适合这样的场景的。我们需要的是NO-block IO。

JDK中提供了两种异步编程的模型:

第一种是Callbacks,异步方法可以通过传入一个Callback参数的形式来在Callback中执行异步任务。比较典型的像是java Swing中的EventListener。

第二中就是使用Future了。我们使用Callable来提交一个任务,然后通过Future来拿到它的运行结果。

这两种异步编程会有什么问题呢?

callback的问题就在于回调地狱。熟悉JS的朋友应该很理解这个回调地狱的概念。

简单点讲,回调地狱就是在callback中又使用了callback,从而造成了这种callback的层级调用关系。

而Future主要是对一个异步执行的结果进行获取,它的 get()实际上是一个block操作。并且不支持异常处理,也不支持延迟计算。

当有多个Future的组合应该怎么处理呢?JDK8 实际上引入了一个CompletableFuture类,这个类是Future也是一个CompletionStage,CompletableFuture支持then的级联操作。不过CompletableFuture提供的方法不是那么的丰富,可能满足不了我的需求。

于是我们的Reactor来了。

Flux

Reactor提供了两个非常有用的操作,他们是 Flux 和 Mono。 其中Flux 代表的是 0 to N 个响应式序列,而Mono代表的是0或者1个响应式序列。

我们看一个Flux是怎么transfer items的:

先看下Flux的定义:

  1. public abstract class Flux<T> implements Publisher<T>

可以看到Flux其实就是一个Publisher,用来产生异步序列。

Flux提供了非常多的有用的方法,来处理这些序列,并且提供了completion和error的信号通知。

相应的会去调用Subscriber的onNext, onComplete, 和 onError 方法。

Mono

我们看下Mono是怎么transfer items的:

看下Mono的定义:

  1. public abstract class Mono<T> implements Publisher<T>

Mono和Flux一样,也是一个Publisher,用来产生异步序列。

Mono因为只有0或者1个序列,所以只会触发Subscriber的onComplete和onError方法,没有onNext。

另一方面,Mono其实可以看做Flux的子集,只包含Flux的部分功能。

Mono和Flux是可以互相转换的,比如Mono#concatWith(Publisher)返回一个Flux,而 Mono#then(Mono)返回一个Mono.

Flux和Mono的基本操作

我们看下Flux创建的例子:

  1. Flux<String> seq1 = Flux.just("foo", "bar", "foobar");
  2. List<String> iterable = Arrays.asList("foo", "bar", "foobar");
  3. Flux<String> seq2 = Flux.fromIterable(iterable);
  4. Flux<Integer> numbersFromFiveToSeven = Flux.range(5, 3);

可以看到Flux提供了很多种创建的方式,我们可以自由选择。

再看看Flux的subscribe方法:

  1. Disposable subscribe();
  2. Disposable subscribe(Consumer<? super T> consumer);
  3. Disposable subscribe(Consumer<? super T> consumer,
  4. Consumer<? super Throwable> errorConsumer);
  5. Disposable subscribe(Consumer<? super T> consumer,
  6. Consumer<? super Throwable> errorConsumer,
  7. Runnable completeConsumer);
  8. Disposable subscribe(Consumer<? super T> consumer,
  9. Consumer<? super Throwable> errorConsumer,
  10. Runnable completeConsumer,
  11. Consumer<? super Subscription> subscriptionConsumer);

subscribe可以一个参数都没有,也可以多达4个参数。

看下没有参数的情况:

  1. Flux<Integer> numbersFromFiveToSeven = Flux.range(5, 3);
  2. numbersFromFiveToSeven.subscribe();

注意,没有参数并不表示Flux的对象不被消费,只是不可见而已。

看下带参数的情况:consumer用来处理on each事件,errorConsumer用来处理on error事件,completeConsumer用来处理on complete事件,subscriptionConsumer用来处理on subscribe事件。

前面的3个参数很好理解,我们来举个例子:

  1. Flux<Integer> ints3 = Flux.range(1, 4);
  2. ints3.subscribe(System.out::println,
  3. error -> System.err.println("Error " + error),
  4. () -> System.out.println("Done"),
  5. sub -> sub.request(2));

我们构建了从1到4的四个整数的Flux,on each就是打印出来,如果中间有错误的话,就输出Error,全部完成就输出Done。

那么最后一个subscriptionConsumer是做什么用的呢?

subscriptionConsumer accept的是一个Subscription对象,我们看下Subscription的定义:

  1. public interface Subscription {
  2. public void request(long n);
  3. public void cancel();
  4. }

Subscription 定义了两个方法,用来做初始化用的,我们可以调用request(n)来决定这次subscribe获取元素的最大数目。

比如上面我们的例子中,虽然构建了4个整数,但是最终输出的只有2个。

上面所有的subscribe方法,都会返回一个Disposable对象,我们可以通过Disposable对象的dispose()方法,来取消这个subscribe。

Disposable只定义了两个方法:

  1. public interface Disposable {
  2. void dispose();
  3. default boolean isDisposed() {
  4. return false;
  5. }

dispose的原理是向Flux 或者 Mono发出一个停止产生新对象的信号,但是并不能保证对象产生马上停止。

有了Disposable,当然要介绍它的工具类Disposables。

Disposables.swap() 可以创建一个Disposable,用来替换或者取消一个现有的Disposable。

Disposables.composite(…​)可以将多个Disposable合并起来,在后面统一做处理。

总结

本文介绍了Reactor的基本原理和两非常重要的组件Flux和Mono,下一篇文章我们会继续介绍Reactor core的一些更加高级的用法。敬请期待。

本文的例子learn-reactive

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/introduction-to-reactor/

本文来源:flydean的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

响应式编程简介之:Reactor的更多相关文章

  1. 响应式编程系列(一):什么是响应式编程?reactor入门

    响应式编程 系列文章目录 (一)什么是响应式编程?reactor入门 (二)Flux入门学习:流的概念,特性和基本操作 (三)Flux深入学习:流的高级特性和进阶用法 (四)reactor-core响 ...

  2. Project Reactor 响应式编程

    目录 一. 什么是响应式编程? 二. Project Reactor介绍 三. Reactor核心概念 Flux 1. just() 2. fromArray(),fromIterable()和 fr ...

  3. 【SpringBoot】SpringBoot2.0响应式编程

    ========================15.高级篇幅之SpringBoot2.0响应式编程 ================================ 1.SprinBoot2.x响应 ...

  4. SpringBoot 2.x (14):WebFlux响应式编程

    响应式编程生活案例: 传统形式: 一群人去餐厅吃饭,顾客1找服务员点餐,服务员把订单交给后台厨师,然后服务员等待, 当后台厨师做好饭,交给服务员,经过服务员再交给顾客1,依此类推,该服务员再招待顾客2 ...

  5. 1小时让你掌握响应式编程,并入门Reactor

    我看同步阻塞 “你知道什么是同步阻塞吗”,当然知道了.“那你怎么看它呢”,这个... 在同步阻塞的世界里,代码执行到哪里,数据就跟到哪里.如果数据很慢跟不上来,代码就停在那里等待数据的到来,然后再带着 ...

  6. Java reactor响应式编程

    转载自:https://www.cnblogs.com/lixinjie/p/a-reactive-streams-on-jvm-is-reactor.html 响应式编程 作为响应式编程方向上的第一 ...

  7. 学习响应式编程 Reactor (1) - 响应式编程

    响应式编程 命令式编程(Imperative Programing),是一种描述计算机所需做出的行为的编程范式.详细的命令机器怎么(How)去处理以达到想要的结果(What). 声明式编程(Decla ...

  8. 学习响应式编程 Reactor (2) - 初识 reactor

    Reactor Reactor 是用于 Java 的异步非阻塞响应式编程框架,同时具备背压控制的能力.它与 Java 8 函数式 Api 直接集成,比如 分为CompletableFuture.Str ...

  9. 学习响应式编程 Reactor (4) - reactor 转换类操作符(1)

    Reactor 操作符 数据在响应式流中的处理,就像流过一条装配流水线.Reactor 既是传送带,又是一个个的装配工或机器人.原材料从源头(最初的 Publisher )流出,经过一个个的装配线中装 ...

随机推荐

  1. mysql-8-subquery

    #进阶8:子查询 /* 出现在其他语句中的select语句(嵌套) 分类: 按子查询出现的位置: SELECT 后面 FROM 后面 WHERE 或 HAVING 后面 EXISTS后面 */ USE ...

  2. pytorch和tensorflow的爱恨情仇之基本数据类型

    自己一直以来都是使用的pytorch,最近打算好好的看下tensorflow,新开一个系列:pytorch和tensorflow的爱恨情仇(相爱相杀...) 无论学习什么框架或者是什么编程语言,最基础 ...

  3. Python编程学习第三课之编程从Hello World开始

    在搞定了前几节课的情况下,大家是否有一种想要跃跃欲试的赶脚,接下来就是我们开始练手的实战时刻. 每个编程人员入门编程的第一课都是向我们马上要进入的编程世界问好,"你好,世界"英文说 ...

  4. Matlab2016b安装流程

    来源:https://jingyan.baidu.com/article/59703552da12ab8fc007402b.html Matlab2016b安装教程 听语音 原创 | 浏览:34338 ...

  5. docker启动镜像报错

    docker启动镜像报错: docker: Error response from daemon: driver failed programming external connectivity on ...

  6. 2017-01-26--编译busybox总结

    错误一: ox@ubuntu:busybox-1.16.0$ make menuconfig Makefile:431: *** mixed implicit and normal rules: de ...

  7. 第一月多测师讲解_ linux_vim命令_004

    一. vi/vim 编辑器共分为三种模式: 命令模式(Command mode),"ESC" 输入模式(Insert mode) 底线命令模式(Last line mode) 命令 ...

  8. Kubernetes K8S之存储ConfigMap详解

    K8S之存储ConfigMap概述与说明,并详解常用ConfigMap示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS ...

  9. Verilog基础入门——简单的语句块编写(二)

    [题干] [代码] module top_module( input a, input b, output out ); assign out = a&b; endmodule [与或非门等] ...

  10. 最大子段和之M子段和

    最大M子段和 题目模型 N个整数组成的序列 \(a_1,a_2,a_3,-,a_n\) ,将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的. 问题分析 方法一: 看到序列,我们首先要尝试 ...