简介

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依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

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

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

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</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为例:

   public static interface Publisher<T> {
public void subscribe(Subscriber<? super T> subscriber);
}
    public static interface Subscriber<T> {

        public void onSubscribe(Subscription subscription);

        public void onNext(T item);

        public void onError(Throwable throwable);

        public void onComplete();
}

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

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

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

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

    public void subscribe(Flow.Subscriber<? super T> subscriber) {
Subscription sub;
if (throwable != null) {
assert iterable == null : "non-null iterable: " + iterable;
sub = new Subscription(subscriber, null, throwable);
} else {
assert throwable == null : "non-null exception: " + throwable;
sub = new Subscription(subscriber, iterable.iterator(), null);
}
subscriber.onSubscribe(sub); if (throwable != null) {
sub.pullScheduler.runOrSchedule();
}
}

上面的例子是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的定义:

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

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

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

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

Mono

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

看下Mono的定义:

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创建的例子:

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

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

再看看Flux的subscribe方法:

Disposable subscribe(); 

Disposable subscribe(Consumer<? super T> consumer); 

Disposable subscribe(Consumer<? super T> consumer,
Consumer<? super Throwable> errorConsumer); Disposable subscribe(Consumer<? super T> consumer,
Consumer<? super Throwable> errorConsumer,
Runnable completeConsumer); Disposable subscribe(Consumer<? super T> consumer,
Consumer<? super Throwable> errorConsumer,
Runnable completeConsumer,
Consumer<? super Subscription> subscriptionConsumer);

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

看下没有参数的情况:

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

numbersFromFiveToSeven.subscribe();

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

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

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

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

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

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

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

public interface Subscription {

    public void request(long n);
public void cancel();
}

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

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

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

Disposable只定义了两个方法:

public interface Disposable {

	void dispose();

	default boolean isDisposed() {
return false;
}

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. 052 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 14 Eclipse下程序调试——debug2 多断点调试程序

    052 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 14 Eclipse下程序调试--debug2 多断点调试程序 本文知识点: Eclipse下程序调 ...

  2. JavaScript innerTHML和createElement效率对比

    前言: 在DOM节点操作中,innerTHML和createElement都可以实现创建元素.它们实现的功能类似,但是效率却相差很大.本文分别统计用innerTHML字符串拼接方式.innerTHML ...

  3. 解决mybatis-plus更新数据的时候,有值为空导致更新失败的问题

    问题 在用mybatis-plus更新数据的时候,更新失败,错误信息如下: Cause: org.apache.ibatis.type.TypeException: Could not set par ...

  4. Docker笔记4:在 CentOS 上安装 Docker

    Docker 是一个开源的应用容器引擎,主要有两个分支,一个是社区免费版(Docker CE),一个是企业版(Docker EE). 第1步:系统环境要求 Docker 支持的 CentOS 版本: ...

  5. 实现Excel文件的上传和解析

    前言 本文思维导图 一.需求描述 实现一个页面上传excel的功能,并对excel中的内容做解析,最后存储在数据库中. 二.代码实现 需求实现思路: 先对上传的文件做校验和解析,这里我们通过Excel ...

  6. mPDF的简单使用

    1. 基本使用 安装 1  在项目下composer文件中 添加 "mpdf/mpdf":"~7.1.9" 测试环境为Yii项目 其他框架composer安装大 ...

  7. 多测师讲解IDE工具python_001.2pycham_安装

    PyCharm安装使用教程 Pycharm 是目前 Python 语言最好用的集成开发工具. 下载 Pycharm 载时有两种版本选择 Professional(专业版,收费)和Community(社 ...

  8. h5的第一份翻译

    <!DOCTYPE html>DOCTYPE DOC文本文档documentTYPE 类型html hyper超,超级的:text文本:markup标记:language语言<htm ...

  9. 【C语言/C++程序员编程】一小时做出来的数字雨(一颗开花的树)!

    相信大家看过许许多多的关于计算机黑客.骇客.人工智能.AI方面的电影,每当黑客入侵某个五角大楼,某个网站时,都会出现这样一副画面: 入侵 或者这样的: 数字雨 然后就轻而易举的成功入侵夺取管理员权限了 ...

  10. docker-搭建 kafka+zookeeper集群

    拉取容器           docker pull wurstmeister/zookeeper           docker pull wurstmeister/kafka     这里演示使 ...