订阅UnicastProcessor永远不会触发

时间:2018-07-16 08:20:08

标签: spring project-reactor

我希望对出现的项目进行批处理,所以我创建了一个UnicastProcessor并像这样订阅了它

UnicastProcessor<String> processor = UnicastProcessor.create()

processor
        .bufferTimeout(10, Duration.ofMillis(500))
        .subscribe(new Subscriber<List<String>>() {
            @Override
            public void onSubscribe(Subscription subscription) {
                System.out.println("OnSubscribe");
            }

            @Override
            public void onNext(List<String> strings) {
                System.out.println("OnNext");
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println("OnError");
            }

            @Override
            public void onComplete() {
                System.out.println("OnComplete");
            }
        });

然后出于测试目的,我创建了一个新线程并开始循环添加项

new Thread(() -> {
    int limit = 100
    i = 0
    while(i < limit) {
        ++i
        processor.sink().next("Hello $i")
    }
    System.out.println("Published all")
}).start()

运行此命令后(让主线程休眠5秒钟),我可以看到所有项目都已发布,但是订阅者未触发任何事件,因此我无法处理任何已发布项目。

我在这里做什么错了?

1 个答案:

答案 0 :(得分:0)

反应流规范是答案!

  

发布者向订阅者发送的onNext总数   必须小于或等于请求的元素总数   始终由该订阅者的订阅。 [Rule 1.1]

在您的示例中,您仅提供了一个不做任何事情的订阅者。反过来,Reactive Streams规范直接说如果您没有调用Subscription#request方法

,将不会发生任何事情(不会进行onNext调用)。
  

订户必须通过Subscription.request(long n)信号要求   接收onNext信号。 [Rule 2.1]

因此,要解决您的问题,一种可能的解决方案是按以下方式更改代码:

UnicastProcessor<String> processor = UnicastProcessor.create()

processor
    .bufferTimeout(10, Duration.ofMillis(500))
    .subscribe(new Subscriber<List<String>>() {
        @Override
        public void onSubscribe(Subscription subscription) {
            System.out.println("OnSubscribe");
            subscription.request(Long.MAX_VALUE);
        }

        @Override
        public void onNext(List<String> strings) {
            System.out.println("OnNext");
        }

        @Override
        public void onError(Throwable throwable) {
            System.out.println("OnError");
        }

        @Override
        public void onComplete() {
            System.out.println("OnComplete");
        }
    });

请注意,在此示例中,大小为Long.MAX_VALUE的需求意味着无限制的需求,因此所有消息都将被直接推送到给定的Subscriber [Rule 3.17]

正确使用UnicatProcessor

一方面,您的示例将在提到的修复程序中正常工作。但是,另一方面,FluxProcessor#sink()的每次调用(是接收器是FluxProcessor的方法)将导致UnicastProcessor的{​​{1}}方法的冗余调用,这在后台导致一些原子读写,如果一次创建onSubscribe并安全地根据需要使用它的驯服次数,则可以避免这些原子读写。例如:

FluxSink

请注意,在此示例中,我执行了一个附加方法UnicastProcessor<String> processor = UnicastProcessor.create() FluxSink<String> sink = processor.serialize().sink(); ... new Thread(() -> { int limit = 100 i = 0 while(i < limit) { ++i sink.next("Hello $i") } System.out.println("Published all") }).start() ,该方法提供线程安全接收器,并确保同时调用serialize不会导致违反ReactiveStreams规范。