响应式编程初识

488 阅读4分钟

以下全部都是基于我个人理解,肯定有错,但是我自己回头看的时候比较容易上手

概述

响应式编程的官方解释是:响应式编程是一种关注于数据流(data streams)和变化传递(propagation of change)的异步编程方式

数据流可以理解为一个无限大的List,而变化传递则是有一个链路来消费这个数据流,而链路之间是可以传递信号的,可以从最底层节点传递一个信号到最上层数据流。

image.png

同样对于处理List, 原本的迭代器模式似乎也可以处理数据,跟迭代器不同的是,响应式流的消费者,数据是主动被推送过来的,而迭代器模式中的消费者需要调用next方法主动的拉取

声明式VS命令式

响应式编程是基于发布订阅模式,数据流就是发布者,而消费者就是订阅者。并且跟迭代器模式是采用命令式的编程方式,而响应式则是采用声明式编程。

如何理解命令式编程和声明式编程。举个例子,A方法调用B方法。命令式编程中我需要在A方法的内部写下调用B方法的命令。a命令b调用,就是命令式编程

static void a(){
    b();
}

static void b(){
    System.out.println("命令式编程");
}

而声明式编程,顾名思义只需要把方法给声明出来,自然有人会给你调用

操作符 Operators

刚才说了,响应式编程是基于发布订阅模式,在生产者和消费者之间可以有很多的操作符来对数据进行改造。而且经过操作符对发布者包装之后会产生一个新的发布者

subscribe() 之前什么都不会发生

在我一开始写响应式代码的时候经常会发现自己的代码没有被执行,其实就是在框架中没有返回流,而没有返回则没有被subscribe,进而自己写的声明式代码,仅仅只是声明了,并没有被消费者消费。

响应式流规范

介绍了一些概念之后我们来看看响应式流是如何定义生产者和消费者之间的关系的。响应式规范定义在 org.reactivestreams:reactive-streams包下

Publisher 生产者

public interface Publisher<T> {

    public void subscribe(Subscriber<? super T> s);
}

生产者中只有一个方法就是添加订阅者

Subscriber 订阅者

public interface Subscriber<T> {
    订阅成功后调用,生产者会传过来一个 subscription
    public void onSubscribe(Subscription s);
    订阅到数据
    public void onNext(T t);
    生产者产生异常
    public void onError(Throwable t);
    生产者发出结束信号
    public void onComplete();
}

订阅者在被注册到生产者之后,生产者会传给订阅者一个 Subscription订阅工具,拿着这个工具就是可以控制生产者的生产数据的速度

Subscription 订阅

public interface Subscription {
    订阅者想要消费多少数据就传入几
    public void request(long n);
    不想消费数据了就调用cancel
    public void cancel();
}

响应式demo

接下来用一个demo来展示如何使用这三个类来编写一个响应式代码

image.png

image.png

image.png

Reactor

刚才说的顶层接口是一个响应式编程的规范,我们自己去写这些逻辑可能就有点太麻烦,Project-Reactor封装了一大堆方法,其中 Publisher 有两个大的实现类 Flux和Mono

image.png

Flux表示有0或N个数据的数据源,而Mono表示0或1个数据的数据源,其他没区别

Flux & Mono

Flux和Mono中的Subscribe方法有很多的重载

subscribe();

subscribe(Consumer<? super T> consumer);

subscribe(Consumer<? super T> consumer, 
            Consumer<? super Throwable> errorConsumer);


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

跟原有的Publisher的接口接受一个Subscriber不同,Flux和Mono对方法进行了重载,接受一些Lambda表达式对数据进行消费,其实这些接受lambda表达式的接口全部都被封装成了一个LambdaSubscriber

image.png

LambdaSubscriber

我们现在来看一下LambdaSubscriber的代码,看代码的方法也还是从Subscriber的那四个接口看起

  1. onSubscribe 当自己被注册到Publisher上的时候

image.png

  1. onNext 当开始消费数据的逻辑

image.png

  1. onError 当产生异常情况时

image.png

  1. onComplete 完成时候的方法,注意这是runnable接口,至于为什么用runnable我也不知道

image.png

需要注意的是LambdaSubscriber就是在被注册到生产者的时候会一口气要全部的数据,然后自己慢慢消费。

这次就先介绍到这里,下一篇就来专门研究Flux和Mono的一些常用API,从源码的角度来分析他们到底是干什么用的