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个重载方法,我们依次来看
- 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可以很好地实现这一点
错误处理
响应式编程中我们必须处理各种异常情况,我们需要有一种方式将异常传播到需要接收的地方。
- onErrorReturn 用一个静态值来返回当出现异常的时候
- onErrorResume 返回另一个流当异常出现的时候
- onErrorMap 把一个异常转换为另一个异常,用于更好地管理
- 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