Flux和Mono的常用API源码分析

Flux

Flux定义了一个普通的响应式流,它可以产生零个,一个或多个元素,乃至无限个元素。

我们就先来研究一下Flux产生元素的代码

Flux.just 产生一个或者多个元素

Flux<String> just = Flux.just("1", "2", "3");


public static <T> Flux<T> just(T... data) {
   return fromArray(data);
}

public static <T> Flux<T> fromArray(T[] array) {
   if (array.length == 0) {
      return empty();
   }
   if (array.length == 1) {
      return just(array[0]);
   }
   return onAssembly(new FluxArray<>(array));
}

Flux.just其实是返回了一个FluxArray对象。那么我们来看看FluxArray的源码是如何。看的关键点是构造方法和subscribe方法

从源码中就可以看出,1个或多个的数据原理就是因为内部有一个数组,这个数组长度可能是1个或多个,然后保存起来通过一个 ArraySubscription 传递给消费者。消费者的代码逻辑可以参照上一篇说的 LambdaSubscriber . 我们继续来看ArraySubscription的逻辑,关键点就是消费者调用 subscription的request之后 subscription是如何把 FluxArray传递给消费者的

Flux.empty() 产生0个元素

public static <T> Flux<T> empty() {
   return FluxEmpty.instance();
}

可以看到调用empty返回的是一个 FluxEmpty实例

@Override
public void subscribe(CoreSubscriber<? super Object> actual) {
   Operators.complete(actual);
}

在收到消费者注册信号的时候调用了 Operators的complete方法

public static void complete(Subscriber<?> s) {
   s.onSubscribe(EmptySubscription.INSTANCE);
   s.onComplete();
}

可以看到在收到注册的时候,给消费者注册了一个EmptySubscription然后马上调用了complete方法

然后在subscription内部什么都没有做。 所以总结起来就是一个0个元素的流,订阅之后会马上收到 complete信号,其他的什么都没有。

repeat() 产生无限流

我们看了1到多个,也看了0个流,现在来看看无限数量的流是如何实现的。

public final Flux<T> repeat() {
   return repeat(ALWAYS_BOOLEAN_SUPPLIER);
}

public final Flux<T> repeat(BooleanSupplier predicate) {
   return onAssembly(new FluxRepeatPredicate<>(this, predicate));
}

可以看到是创建了一个 FluxRepeatPredicate 对象 这个对象是一个 操作符

源码也不难懂,就是结束的时候重新subscribe了Publisher

doOnSignal

Flux.just("1", "2", "3")
        .doOnNext(data->{
            
        })
        .doOnComplete(()->{
            
        });

我们再来看一个常用的Api,很多时候我们不用真正的消费或者是转变数据,就像Java8中流式编程中的peek一样。Reactor也提供了peek的机制

直接去看源码,一样这个 FluxPeekFuseable 也是一个操作符。我们就看一个peek onNext的代码就知道个大概

Mono

Mono表示要么就有一个元素,要么就只会产生完成和错误信号的Publisher

then

then是一个在框架中非常常用的一个Api,这里有5个重载方法,我们依次来看

  1. then()
public final Mono<Void> then() {
   return empty(this);
}

static <T> Mono<Void> empty(Publisher<T> source) {
   @SuppressWarnings("unchecked")
   Mono<Void> then = (Mono<Void>)ignoreElements(source);
   return then;
}


public static <T> Mono<T> ignoreElements(Publisher<T> source) {
   return onAssembly(new MonoIgnorePublisher<>(source));
}

一路看下来我们发现其实是创建了一个 MonoIgnorePublisher 这个对象是一个 操作符

通过源码我们可以看到 MonoIgnorePublisher 把真正的监听者封装了一个IgnoreElementsSubscriber 然后让事件源监听。我们来看看这个类

又是Subscription还是Subscriber

用图像表示then的情况就是

总结一下就是then方法会丢弃前面的onNext数据,只会传递给下游完成和错误的信号

then(Mono<V> other)


Mono.just("1").then(Mono.just("haha")).subscribe();

public final <V> Mono<V> then(Mono<V> other) {
   return onAssembly(new MonoIgnoreThen<>(new Publisher[] { this }, other));
}

可以看到内部是让 MonoIgnoreThen 成为Publisher

其内部有创建了一个 ThenIgnoreMain 关键注意后面调用的方法subscribeNext

可以看到then(Mono<V> other) 的逻辑其实就是忽略之前的数据,然后全部执行结束之后开始下一个流的消费

Create

Mono和Flux都有Create的方法,用于创建一个对应的序列。我们就来研究mono的create方法

Mono.<String>create(sink->{
    try {
        int i = 1 / 0;
        sink.success(i);
    }catch (Exception e){
        sink.error(e);
    }
}).subscribe();


public static <T> Mono<T> create(Consumer<MonoSink<T>> callback) {
    return onAssembly(new MonoCreate<>(callback));
}

可以看到create其实是创建一个MonoCreate对象,里面传入一个带有MonoSink 的一个comsumer 我们来看看MonoSink的API

public interface MonoSink<T> {

   void success();

   void success(@Nullable T value);

   void error(Throwable e);

   @Deprecated
   Context currentContext();

   default ContextView contextView() {
      return this.currentContext();
   }

   MonoSink<T> onRequest(LongConsumer consumer);

   MonoSink<T> onCancel(Disposable d);

   MonoSink<T> onDispose(Disposable d);
}

其实就是定义了一些操作这个流的一些API。继续看MonoCreate对象的内容

正在上传…重新上传取消

using

using实现了 try-with-resource 机制, 假设需要包装一个阻塞的API

class Connection implements AutoCloseable{
    private final Random rnd = new Random();

    public Iterable<String> getData(){
        if (rnd.nextInt(10)< 3){
            throw new RuntimeException("");
        }
        return Arrays.asList("some","data");
    }
    @Override
    public void close()  {
        System.out.println("close");
    }

    public static Connection newConnection(){
        System.out.println("create connection");
        return new Connection();
    }
}

使用using可以很好地实现这一点

错误处理

响应式编程中我们必须处理各种异常情况,我们需要有一种方式将异常传播到需要接收的地方。

  1. onErrorReturn 用一个静态值来返回当出现异常的时候
  2. onErrorResume 返回另一个流当异常出现的时候
  3. onErrorMap 把一个异常转换为另一个异常,用于更好地管理
  4. retry 重新订阅数据源

热数据流和冷数据流

Publisher分为冷发布者和热发布者,他们的区别是 冷发布者在没有订阅者的时候不会生成数据,而热发布者不论有没有订阅者都会生成数据

多播流元素

我们可以转换冷热发布者,例如使用 defer将热操作符转换为冷操作符。也可以使用 ConnectableFlux 将冷操作符转换为热操作符

ConnectableFlux<Integer> publish = Flux.range(1, 3)
        .doOnSubscribe(s -> System.out.println("收到了订阅"))
        .publish();


publish.subscribe(System.out::println);
publish.subscribe(System.out::println);

publish.connect();

上面的代码只会收到一次的订阅,但是可以同时给两个消费。

源码分析:代码的关键就是publish的时候如何转变为 connectableFlux, 在subscribe的时候做了什么,还有一个就是connect方法

public final ConnectableFlux<T> publish(int prefetch) {
   return onAssembly(new FluxPublish<>(this, prefetch, Queues
         .get(prefetch)));
}

我们可以看到调用publish其实是创建了一个FluxPublish,传入的参数是256,第三个参数是

static final Supplier SMALL_SUPPLIER = () -> Hooks.wrapQueue(new SpscArrayQueue<>(SMALL_BUFFER_SIZE));

传入了一个被包装了的Queue,所以我们知道publish方法其实是创建了一个FluxPublish对象。接下来看subscribe的时候做了什么

从代码中可以看出FluxPublish中有一个父subscriber叫PublishSubscriber每次注册进来一个都会被添加到父容器中。接下来看connect方法

可以看到在connect的时候,才真正地订阅了数据源。那么数据源来了之后,就是看 PublishSubscriber的onSubscribe方法

可以看到父subscriber一次拉取了256个数据,接下来就是看onNext方法

用图表示

如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,,咱们下期见。

收藏 等于白嫖,点赞才是真情

作者:马尾区卷王
链接:
https://juejin.cn/post/7107904489096478751

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值