本系列其他文章见:《响应式Spring的道法术器》。
前情提要:响应式流 | Reactor3快速上手 | 深入理解响应式流规范
2.2 自定义数据流
这一小节介绍如何通过定义相应的事件(onNext
、onError
和onComplete
) 创建一个 Flux 或 Mono。Reactor提供了generate
、create
、push
和handle
等方法,所有这些方法都使用 sink(池)来生成数据流。
sink,顾名思义,就是池子,可以想象一下厨房水池的样子。如下图所示:
下面介绍到的方法都有一个sink提供给方法使用者,通常至少会暴露三个方法给我们,next
、error
和complete
。next和error相当于两个下水口,我们不断将自定义的数据放到next口,Reactor就会帮我们串成一个Publisher数据流,直到有一个错误数据放到error口,或按了一下complete
按钮,数据流就会终止了。
本文测试源码。
2.2.1 generate
generate
是一种同步地,逐个地发出数据的方法。因为它提供的sink是一个SynchronousSink
, 而且其next()
方法在每次回调的时候最多只能被调用一次。
generate
方法有三种签名:
public static <T> Flux<T> generate(Consumer<SynchronousSink<T>> generator)
public static <T, S> Flux<T> generate(Callable<S> stateSupplier, BiFunction<S, SynchronousSink<T>, S> generator)
public static <T, S> Flux<T> generate(Callable<S> stateSupplier, BiFunction<S, SynchronousSink<T>, S> generator, Consumer<? super S> stateConsumer)
1)使用SynchronousSink生成数据流
@Test
public void testGenerate1() {
final AtomicInteger count = new AtomicInteger(1); // 1
Flux.generate(sink -> {
sink.next(count.get() + " : " + new Date()); // 2
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count.getAndIncrement() >= 5) {
sink.complete(); // 3
}
}).subscribe(System.out::println); // 4
}
- 用于计数;
- 向“池子”放自定义的数据;
- 告诉
generate
方法,自定义数据已发完; - 触发数据流。
输出结果为每1秒钟打印一下时间,共打印5次。
2)增加一个伴随状态
对于上边的例子来说,count
用于记录状态,当值达到5之后就停止计数。由于在lambda内部使用,因此必须是final类型的,且不能是原生类型(如int
)或不可变类型(如Integer
)。
如果使用第二个方法签名,上边的例子可以这样改:
@Test
public void testGenerate2() {
Flux.generate(
() -> 1, // 1
(count, sink) -> {
// 2
sink.next(count + " : " + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count >= 5) {
sink.complete();
}