【问题标题】:How to ensure reactor flux process all the messages supplied如何确保反应堆通量处理提供的所有消息
【发布时间】:2017-11-09 02:25:32
【问题描述】:

假设我们想让 Flux 管道来处理从多个线程提供的所有消息。让我们考虑下面的代码:

@Test
public void testFluxCreate() throws InterruptedException {
    EmitterProcessor<String> processor = EmitterProcessor.create();
    CountDownLatch latch = new CountDownLatch(1);

    AtomicLong counter = new AtomicLong();
    AtomicLong batch = new AtomicLong();
    Flux<List<String>> flux = processor
            .doOnSubscribe(ss -> System.out.println(nm() + " : subscribing to + ss))
            .onBackpressureError()
            .buffer(7)
            .publishOn(Schedulers.immediate())
            .doOnNext(it -> {
                counter.addAndGet(it.size());
                System.out.println(batch.incrementAndGet() + " : " + nm() + "Batch: " + it.size());
            })
            ;

    CompletableFuture<Void> producer = CompletableFuture.runAsync(() -> {
        IntStream.range(1, 1001).forEach(it -> {
            //sleep();
            processor.onNext("Message-" + it);
        });
    });

    CompletableFuture<Void> producer2 = CompletableFuture.runAsync(() -> {
        IntStream.range(1, 1001).forEach(it -> {
            //sleep();
            processor.onNext("Message2-" + it);
        });
    });

    CompletableFuture<Void> future = CompletableFuture.allOf(producer, producer2).thenAccept(it -> processor.onComplete());

    flux.doOnComplete(latch::countDown).subscribe();

    future.join();
    latch.await();

    System.out.println("Total: " + counter);
}

计数器告诉我们,每次执行此代码时,实际处理的消息数都是不同的。 这个实现有什么问题? 我们如何确保在程序结束之前处理所有消息?

【问题讨论】:

  • 尚未正确查看,但.onBackpressureError() 在处理不够快时故意丢弃事件。你知道吗?

标签: reactive-programming project-reactor


【解决方案1】:

这个实现有什么问题?

当我运行代码时,我会在启动后的早期日志中看到以下内容:

18:39:12.590 [ForkJoinPool.commonPool-worker-1] DEBUG reactor.core.publisher.Operators - Duplicate Subscription has been detected
java.lang.IllegalStateException: Spec. Rule 2.12 - Subscriber.onSubscribe MUST NOT be called more than once (based on object equality)
    at reactor.core.Exceptions.duplicateOnSubscribeException(Exceptions.java:162)
    at reactor.core.publisher.Operators.reportSubscriptionSet(Operators.java:502)
    at reactor.core.publisher.Operators.setOnce(Operators.java:607)
    at reactor.core.publisher.EmitterProcessor.onNext(EmitterProcessor.java:245)
    at de.schauder.reactivethreads.demo.StackoverflowQuicky.lambda$null$2(StackoverflowQuicky.java:54)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
    at de.schauder.reactivethreads.demo.StackoverflowQuicky.lambda$main$3(StackoverflowQuicky.java:52)

我不熟悉 EmitterProcessor,但似乎 onNext 不是线程安全的,我强烈怀疑这是导致事件丢失的原因。

我们如何确保在程序结束之前处理完所有消息?

我会使用两个单独的 Producersmerge。另外我认为您不需要倒计时闩锁。

public static void main(String[] args) {

    AtomicLong counter = new AtomicLong();
    AtomicLong batch = new AtomicLong();

    EmitterProcessor<String> processor1 = EmitterProcessor.create();
    EmitterProcessor<String> processor2 = EmitterProcessor.create();

    Thread thread1 = constructThread(processor1);
    Thread thread2 = constructThread(processor2);


    Flux<List<String>> flux = processor1.mergeWith(processor2)
            .buffer(7)
            .onBackpressureError()
            .publishOn(Schedulers.immediate())
            .doOnNext(it -> {
                counter.addAndGet(it.size());
                System.out.println(batch.incrementAndGet() + " : Batch: " + it.size());
            }).doOnComplete(() -> {
                System.out.println("Total count: " + counter.get());
            });

    thread1.start();
    thread2.start();

    flux.blockLast();
}

private static Thread constructThread(EmitterProcessor<String> processor) {
    return new Thread(() -> {
        IntStream.range(1, 1001).forEach(it -> {
            processor.onNext("Message2-" + it);
        });
        processor.onComplete();
    });
}

注意我的评论:

onBackpressureError() 导致Flux 在订阅者无法足够快地处理所有事件时发出错误,因此这可以解释不匹配,但您会看到异常。

【讨论】:

  • 我看不到任何异常,所以似乎背压不是一个案例。我检查了你关于 onNext 线程安全的建议,你是对的!谢谢!下一步是确定如何以非阻塞方式解决一个订阅者和多个生产者的任务 =) 了解如何使用 project-reactor 实现它是最终目标。
  • blockLast 只是在那里实际等待Flux 的结束。所以它真的只是为了防止应用程序结束。就像倒计时闩锁一样,但以更惯用的方式。
猜你喜欢
  • 2019-11-07
  • 2016-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多