project reactor (1)
注意,本文讲解一句官方文档 https://projectreactor.io/docs/core/release/reference/#flux;
同时对于其中过期的api也会进行适当扩展。
核心
flux
an Asynchronous Sequence of 0-N Items
一个表示 包含0-n个元素的 异步序列。
mono
an Asynchronous 0-1 Result
一个表示 包含1个元素或者没有元素的 异步结果。
创建并订阅flux和mono
创建
Flux<String> seq1 = Flux.just("foo", "bar", "foobar");
List<String> iterable = Arrays.asList("foo", "bar", "foobar");
Flux<String> seq2 = Flux.fromIterable(iterable);
Mono<String> noData = Mono.empty();
Mono<String> data = Mono.just("foo");
// 第一个参数是范围的开始,而第二个参数是要生成的itmes数。
Flux<Integer> numbersFromFiveToSeven = Flux.range(5, 3);
订阅
subscribe 使用了java8的lambda函数表达式,若不熟悉java8函数表达式以及stream流操作api的童鞋,强烈建议学习一下!不光是为了下面的内容铺垫,它真的很厉害!
包含以下变种:
// 订阅并触发序列。
subscribe();
// 为每个产生的value做一些逻辑。
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 调用产生的Subscription 执行某些操作。
subscribe(Consumer<? super T> consumer,
Consumer<? super Throwable> errorConsumer,
Runnable completeConsumer,
Consumer<? super Subscription> subscriptionConsumer);
这些变体返回对subscribe 的引用,我们可以在不需要更多数据时使用该引用来取消订阅。 取消后,源应停止产生值并清除其创建的任何资源。
这种取消和清理行为在Reactor中由通用的Disposable接口表示。
subscribe 示例(五个变种)
1 简单操作
// 设置一个Flux,当添加一个订阅者之后,该Flux会产生三个值。
Flux<Integer> ints = Flux.range(1, 3);
// 以最简单的方式订阅。
ints.subscribe();
2 消费操作
Flux<Integer> ints = Flux.range(1, 3);
// 订阅者订阅并打印对应的value
ints.subscribe(System.out::println);
3 2+error回调
Flux<Integer> ints = Flux.range(1, 4)
.map(i -> {
if (i <= 3) {
return i;
}
throw new RuntimeException("Got to 4");
});
ints.subscribe(
System.out::println,
error -> System.err.println("Error: " + error)
);
输出:
1
2
3
Error: java.lang.RuntimeException: Got to 4
4 3+completion events 回调
Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(
System.out::println,
error -> System.err.println("Error " + error),
// 添加 completion events 处理器
() -> System.out.println("complete Done")
);
输出:
1
2
3
4
complete Done
注意:error 信号和complete 信号都是 终端事件(stream中的概念),并且彼此互斥。 为了使完成completion
events 处理器工作,因此示例代码没有触发error 信号。
complete 回调没有输入,由一对空括号表示:它与Runnable接口中的run方法匹配。
5 4+Subscription 操作
该变体需要您对Subscription进行操作(对它执行一个request(long)或cancel())。 否则,flux会挂起。
Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(System.out::println,
error -> System.err.println("Error " + error),
() -> System.out.println("Done"),
// 当我们订阅时,我们会收到一个 Subscription。这里表示我们要从源中获取最多10个元素(实际上将发出4个元素并完成)。
sub -> sub.request(10));
注意,新版3.5 的api已经弃用此变种,官方的解释是使用者经常会忘记 subsciption,因此如果确实需要的话官方建议使用 subscribeWith(Subscriber) 代替。
就像这样就不会忘记啦/捂脸,怎么会有种奇葩的感觉:
ints.subscribeWith(new Subscriber<Integer>() {
@Override
public void onNext(Integer integer) {
System.out.println(integer);
}
@Override
public void onError(Throwable throwable) {
System.err.println("Error " + throwable);
}
@Override
public void onComplete() {
System.out.println("Done");
}
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(10);
}
});
使用 返回的Disposable取消一个 subscribe()
以上那些基于lambda的 subscribe()变体都具有 Disposable 返回类型。因此在这种情况下,可以通过调用其dispose()方法来取消订阅。
对于flux和mono来说,cancel 是让信号源应停止产生 元素 的信号。 但是这个操作并不能保证立即执行:某些源可能以很快的速度产生元素,以至于在接收到取消指令之前已经完成了所有的生产动作。
Disposables类中提供了一些有关Disposable的实用程序。其中,Disposables.swap()创建一个Disposable包装器,使我们可以原子地取消和替换一个具体的Disposable。
另一个有趣的实用程序是Disposables.composite(…)。 通过此组合,可以收集多个Disposable(例如,与服务调用关联的多个进行中的请求),并在以后一次性处理所有这些请求。 调用组合的dispose()方法后,任何尝试添加另一个Disposable都会立即被dispose。
lambda的替代方案 BaseSubscriber
还有一种附加的订阅方法,该方法更通用并且采用成熟的订阅服务器,而不是从一个lambda表达式中生成。
BaseSubscriber(或其子类)的实例是一次性的,这意味着,如果BaseSubscriber订阅了第二个发布者,则取消其对第一个发布者的订阅。 这是因为使用实例两次将违反“反应式流”规则,即不得并行调用订阅服务器的onNext方法。
示例:
package cn.lucky.reactor.demo1;
import org.reactivestreams.Subscription;
import reactor.core.publisher.BaseSubscriber;
/**
默认情况下,BaseSubscriber会触发一个无限制的请求,其行为与Subscribe()完全相同。
但是,当需要自定义请求量时,扩展BaseSubscriber会更加有用。
**/
public class SampleSubscriber<T> extends BaseSubscriber<T> {
@Override
public void hookOnSubscribe(Subscription subscription) {
System.out.println("Subscribed");
// 自定义请求量 一次一个请求
request(1);
}
@Override
public void hookOnNext(T value) {
System.out.println(value);
// 自定义请求量 一次一个请求
request(1);
}
}
测试:
SampleSubscriber<Integer> ss = new SampleSubscriber<Integer>();
Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(ss);
输出:
Subscribed
1
2
3
4
BaseSubscriber还提供了requestUnbounded()方法以切换到无界模式(等效于request(Long.MAX_VALUE)),当然还提供了cancel()方法。
另外和lambda中的变种类似,它还具有其他钩子:hookOnComplete,hookOnError,hookOnCancel和hookFinally(在序列终止时始终调用,终止类型作为SignalType参数传递)。对于这些钩子方法童鞋可自行实现,示例中的 SampleSubscriber 是执行绑定请求的订阅服务器的绝对最小实现。
Backpressure 和重塑 requests
在Reactor中实现Backpressure 时,通过向上游 operator发送 request ,将consumer pressure 传播回source 。 当前请求的总和有时被称为当前“需求”或“待处理请求”。 需求的上限为Long.MAX_VALUE,表示无限制的请求(意味着“尽快生成”,这样基本上是禁用Backpressure )。
第一个请求在订阅时来自 final 订阅者,但是下面这些最直接的订阅方式立即触发了Long.MAX_VALUE的无限制请求:
- subscrbe()及其大多数基于lambda的变体(具有Consumer 的变量除外)
- block(), blockFirst() 和 blockLast()
- 通过toIterable() 或者 toStream() 进行遍历
定制原始请求的最简单方法是使用BaseSubscriber进行subscribe,并覆盖hookOnSubscribe方法,如以下示例所示:
Flux.range(1, 10)
.doOnRequest(r -> System.out.println("第 " + r +" 个请求"))
.subscribe(new BaseSubscriber<Integer>() {
@Override
public void hookOnSubscribe(Subscription subscription) {
request(1);
}
@Override
public void hookOnNext(Integer integer) {
System.out.println("收到 " + integer +" 之后cancel ");
cancel();
}
});
输出:
第 1 个请求
收到 1 之后cancel
在处理请求时,必须小心以产生足够的需求来推进序列,否则您的Flux可能会“卡住”。 这就是为什么BaseSubscriber在hookOnSubscribe中默认为无界请求的原因。 覆盖此挂钩时,通常应至少调用一次请求。
改变下游需求的Operators
要记住的一件事是,上游链中的每个Operator 都可以调整在订阅级别表达的需求。 一个教科书式的例子是 buffer(N)运算符:如果它接收到request(2),则将其解释为对两个完整缓冲区的需求。 结果,由于缓冲区需要将N个元素视为已满,因此缓冲区运算符会将请求重塑为2 xN。