Reactor 简介
Reactor 是完全异步非阻塞的响应式编程 JVM 框架,它能通过背压 backpressure 的方式来实现对元素需求的管理。
它提供了跟Java8 函数式接口的集成,例如 CompletableFuture, Stream 和 Duration;并主要提供两个异步序列接口 Flux(N个元素) 和 Mono(0-1个元素),并严格遵循响应式流 Reactive Streams 的JVM规范。
核心依赖
需要JDK基线版本8或以上
在pom中引入下列库依赖进行使用
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.21</version>
</dependency>
若需要进行测试用例的编写,加入下列依赖
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.4.10</version>
</dependency>
Mono/Flux 创建方式
just
简单元素生成
Flux.just("a", "b", "c");
fromIterable
从 Iterable 接口实现类(能够使用for-each循环的数据结构)生成。Mono只有0-1个元素,所以语义定义上没有从Iterable创建的必要。
Flux.fromIterable(Arrays.asList("a", "b", "c"));
fromArray
从数组生成
Flux.fromArray(new String[]{"a", "b", "c"});
fromStream
此处为与Java Stream的集成
Flux.fromStream(Stream.of(new String[]{"a", "b", "c"}));
generate
generate 是同步且逐个发送元素的流创建方法,逐次发送意味着每次回调最多只能调用一次 next() 方法(虽然可以有选择地调用 error(Throwable) 或 complete())
一般情况下我们会使用一个变量记录当前 sink 的状态。
Flux.generate(
AtomicLong::new,
(state, sink) -> {
long i = state.getAndIncrement();
sink.next(i);
if (i == 10) sink.complete();
return state;
},
(state) -> System.out.println("I'm done")
);
create
create 跟 generate 的不同点在于
- create 在一次方法调用中可以多次调用 next() 发送多个元素
- 没有表达可变状态的变量
一般它被用于转化其他异步的 API(比如listener)将其集成到 reactor框 架中来,并且我们可以在 create 方法中指定背压策略 OverflowStrategy
Flux.create(sink -> {
//pretend we are registering a listener here
new MyDataListener(){
public void onReceiveData(String str){
sink.next(str);
}
public void onComplete(){
sink.complete();
}
};
}, FluxSink.OverflowStrategy.DROP);
常用的背压策略有下列几种:
IGNORE
忽略下游的背压请求ERROR
下游消费过慢的时候会抛出一个IllegalStateExceptionDROP
下游消费过慢的时候,就drop掉这个信号LATEST
下游指挥拿到上游发送的最新的信号,这个如果是一个实时信息流的话会很有用BUFFER
也就是默认策略,如果下游消费过慢的话,就会存进一个缓存,要注意这个缓存是会无限增长的,堆积过多会内存溢出
push
和 create 类似,不过当中的 next,complete,error 方法只能由一个线程调用,适用于单一 Producer 的场景
其他
Flux#range
从整数范围进行遍历Mono#justOrEmpty
从可能是null值的生成元素Mono#fromSupplier
从Supplier生成单一元素Mono#fromRunnable
从一个Runnable Task生成单一元素Mono#fromFuture
从CompletableFuture生成单一元素empty
空元素集error
立即抛出异常defer
推迟到订阅时执行using
从disposable source生成元素流
操作Mono/Flux
map
通过 mapper 对元素进行转换操作
map(Function<? super T, ? extends V> mapper)
Flux<Integer> ints = Flux.range(1, 4);
Flux<Integer> mapped = ints.map(i -> i * 2);
filter
通过给定 Predicate 对每个元素进行判断,如果条件为真,则该元素会被发送;反之被忽略
filter(Predicate<? super T> p)
Flux<Integer> ints = Flux.range(1, 4);
Flux<Integer> filtered = ints.filter(i -> i % 2 == 0);
buffer
将上游元素分段,当元素积累了一定数量后一并发送(组成列表,注意 buffer 后的类型 List)
Flux<Integer> ints = Flux.range(1, 40);
Flux<List<Integer>> buffered = ints.buffer(3);
retry
在响应式流传递过程中报错时重新订阅这个发布(在 Flux 中要注意元素重复消费的问题)
Flux.log().retry(3);
zip
将两个响应式流合并成一个,直到其中任意一个流元素耗尽,这个是非常有用的。
Flux<Integer> fluxA = Flux.range(1, 4);
Flux<Integer> fluxB = Flux.range(5, 5);
fluxA.zipWith(fluxB, (a, b)-> a+b)
其他
cast
对元素进行强制类型转换Flux#index
对元素在流中的位置下标进行注明(从0开始),返回 Tuple2<Long, T>类型的Flux。flatMap
将元素进行1对多的映射,可以是异步的方法调用(map只能是同步映射)Flux#startWith
在元素序列开头添加元素Flux#concatWithValues
在元素序列结尾添加元素Flux#collectList
将Flux聚合成一个ListFlux#collectMap
将Flux聚合成一个MapFlux#count
计算序列的大小Flux#reduce
对相邻元素进行计算(归约)Flux#take
取一个序列从头开始的n个元素Flux#takeLast
取一个序列最后的的n个元素Flux#skip
跳过n个元素Flux#takeUntil
取元素直到满足条件(类似repeat until循环)Flux#takeWhile
当满足条件时取元素(类似while循环)
观察Mono/Flux
这里的观察是指对序列进行观察且不会改变其中元素的状态
doOnNext
在序列向下游传送onNext时进行调用doOnComplete
在序列向下游传送onComplete时进行调用doOnError
在序列向下游传送onError时进行调用doOnCancel
在Subscription.cancel时执行该回调(先于cancel信号向上游发送前执行)doFirst
在序列开始前执行doOnSubscribe
在Subscription开始后,它比doFirst执行要晚doOnRequest
在Flux/Mono接收到(请求元素)request时调用(先于request信号向上游发送前执行)doOnTerminate
在Flux/Mono结束(无论成功与否)执行(先于onComplete/onError信号向下游发送前执行)doAfterTerminate
在Flux/Mono结束(无论成功与否)执行(后于onComplete/onError信号向下游发送前执行)doOnEach
任何元素发送前皆会执行(onComplete/onError/onNext)doFinally
Flux/Mono因为任何原因(COMPLETE/ERROR/CANCEL)终结后皆会执行log
记录内部日志(常用于调试)
Hot流和Cold流
Flux 和 Mono 代表元素的异步序列,我们只有在调用subscribe方法时才会对其中元素进行处理;
但实际上,根据他们产生元素的条件,我们可以将Publisher划分成两类:
Cold
每次订阅都会生成新的数据,如果没有订阅,那么元素不会被生成。Hot
即使在没有订阅者的情况下也会生成元素,他们会持续向下游发送数据,订阅者只能观察到注册订阅后的元素。
这种 Hot(实时流)和 Cold(非实时流)的划分,可以更精确的描述现实中元素产生的场景。