Reactor3 Flux.create与Flux.push正确打开方式

前言

Reactor3的生产元素有很多种方式。比如常用的Flux.just等等。但是Flux.just是一个热数据源,它的元素在调用Flux.just的时候就已经确定了,并且无论多少个订阅者订阅多少次,它们拿到的元素都是一样的。如果有一个生产者和一个消费者,在发生订阅的时候生产者可能还不知道要下发的元素是什么,可能要等一个事件来触发消息的下发,当这个事件到来需要马上下发给消费者,这个时候Flux.just就无能为力了。这个时候就需要用到Flux.create或者Flux.push了。


版本

Reactor 3.4.9

阅读须知

虽然本篇是Flunx.createFlux.push的最佳实践,后面笔者都是拿create来举例与说明,在本篇的例子不涉及到两者区别的地方,读者可以将create直接替换成push,效果是一样的。两者的区别会在下篇文章说明。


抛出问题

一个生产者,一个消费者。可以往生产者不断的提交元素,然后由消费者来进行消费。这个被提交的元素在创建生产者是不知道,可能后面某个时候由某个事件触发。就好比12306放票一样,火车票在某个时间点触发放票事件,然后丢到队列,由消费者执行放票后的逻辑。


简单的例子

Flux.create(sink -> {
    for (int i = 0; i < 10; i++) {
        sink.next(i);
    }
    sink.complete();
}).subscribe(System.out::println);

上面的例子很容易看懂,在一个for循环中使用sink来下发元素然后结束。
很多文章甚至书籍都是拿这样的例子来介绍Flux.create(),这样的用法和Flux.just其实没有太大的区别,在创建的时候就已经确定了怎么去下发元素。这样例子其实并不能让我们真正的使用Flux.create去解决实际开发中的问题,意义不大。


一个Bad的解决方案

上面的例子之所以不能解决抛出的问题,关键在于下发元素只能在create的函数表达式中进行,也就是函数式的参数FluxSink,那如果在这个函数式中将FluxSink开放出来呢?

	FluxSink<String> outSink;
    @Test
    public void testFluxCreate() {
        Flux<String> f = Flux.create(sink -> {
            outSink = sink;
        });
        f.subscribe(e -> System.out.println(e))
        //do something

        //下发元素
        outSink.next("我来了");

        outSink.next("我来了2");
    }
  1. 声明一个FluxSink类型成员变量outSink,用来接收Flux.create中的sink
  2. Flux.create函数式方式中将sink赋值给成员变量
  3. 在外部通过outSink随时下发元素。
    这样确实能解决上面的抛出的问题,但是也引发了其他的问题。我们只是想进行元素的下发,但是将FluxSink开放出来它可不止能进行元素下发,还有其他的方法。这样做破坏了封装性,如果其他人使用不当,比如提前结束FluxSink等等会引发异常的Bug。

使用的官网例子来解决

通过上面的例子,我们可以得到一个结论:不能把FluxSink放出来。问题只能在内部解决,不能公开😂。
在Reactor3 Guide中有下面这一段Flux.create的用法。

interface MyEventListener<T> {
    void onDataChunk(List<T> chunk);
    void processComplete();
}
Flux<String> bridge = Flux.create(sink -> {
    myEventProcessor.register( 
      new MyEventListener<String>() { 

        public void onDataChunk(List<String> chunk) {
          for(String s : chunk) {
            sink.next(s); 
          }
        }
        public void processComplete() {
            sink.complete(); 
        }
    });
});

有一个监听者的接口,在createlambda表达式中创建一个监听者并将它注册到监听器中。而这个监听者在事件触发的时候就会调用sink的下发元素的方法。它并没有将FluxSink的直接暴露出去,而是使用一个订阅者对它进行了一层封装,只暴露一些需要的方法。


简化后的方案

上面我们看到虽然能解决问题,但是需要一个监听者接口并实现它,还需要一个监听器,如果是一个简单的问题,只有一个监听者需要这么步骤就显得太复杂了。那我们可以结合第一个Bad方案和官方的方案得到一个简化的方案:

	Consumer<String> producer;

    @Test
    public void testFluxCreate() {
        Flux.create(sink -> {
            producer = nextData -> sink.next(nextData);
        }).subscribe(e -> System.out.println(e));

        //do something

        //下发元素
        producer.accept("我来了");

        producer.accept("我来了2");

    }
  1. 声明一个Consumer类型成员变量producer,在Flux.create中进行初始化,定义如何使用sink
  2. 在外部通过producer随时下发元素。
    上面的例子我们即简单的解决了上面抛出的问题,也没有破话封装性,只暴露关键的功能出来。

总结

本篇介绍了使用Flux.create与Flux.push去解决一些实际开发中会遇到的问题,正确的使用它们并且发挥出它们的真实价值。而不是像其他文章那样简单的使用埋没了它们的真实能力。

感谢阅读,希望对你有帮助。

参考资料

Reactor3 Guide

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值