Project Reactor:处理快速和慢速发布者

时间:2019-07-29 14:34:14

标签: java reactive-programming project-reactor reactive

考虑以下代码:

AtomicInteger counter1 = new AtomicInteger();
AtomicInteger counter2 = new AtomicInteger();

Flux<Object> source = Flux.generate(emitter -> {
    emitter.next("item");
});

Executor executor1 = Executors.newFixedThreadPool(32);
Executor executor2 = Executors.newFixedThreadPool(32);

Flux<String> flux1 = Flux.merge(source).concatMap(item -> Mono.fromCallable(() -> {
        Thread.sleep(1);
        return "1_" + counter1.incrementAndGet();
}).subscribeOn(Schedulers.fromExecutor(executor1)));

Flux<String> flux2 = Flux.merge(source).concatMap(item -> Mono.fromCallable(() -> {
    Thread.sleep(100);
    return "2_" + counter2.incrementAndGet();
}).subscribeOn(Schedulers.fromExecutor(executor2)));

Flux.merge(flux1, flux2).subscribe(System.out::println);

您会看到一个发布者的速度是另一发布者的100倍。尽管如此,运行代码时似乎所有数据都已打印,但是两个发布者之间仍然存在巨大差距,这会增加超时时间。

值得注意的是,在更改数字时,executer2将具有1024个线程,而executer1将仅具有1个线程,那么我们仍然看到了差距加班越来越大。

我的期望是,在相应地调整了线程池之后,发布者将变得平衡。

  1. 我想在发布者之间取得平衡(相对于线程池大小和处理时间)

  2. 如果我等待足够长的时间会发生什么?换句话说,会产生背压吗? (默认情况下,我认为这是运行时异常,对吗?)

我不想删除项目,也不想有运行时异常。相反,如上所述,我希望系统在资源和处理时间方面达到平衡-上面的代码是否可以保证?

谢谢!

1 个答案:

答案 0 :(得分:1)

在此示例中,您的Flux对象不是ParallelFlux对象,因此它们只能使用一个线程。

是否创建能够处理数千个线程的调度程序并将其传递给Flux对象之一都没关系-它们只是闲置在那里,这正是发生在其中的事情这个例子。没有背压,也不会导致异常-它只使用一个线程就可以运行得最快。

如果要确保Flux充分利用了可用的1024个线程,则需要调用.parallel(1024)

ParallelFlux<String> flux1 = Flux.merge(source).parallel(1).concatMap(item -> Mono.fromCallable(() -> {
    Thread.sleep(1);
    return "1_" + counter1.incrementAndGet();
}).subscribeOn(Schedulers.fromExecutor(executor1)));

ParallelFlux<String> flux2 = Flux.merge(source).parallel(1024).concatMap(item -> Mono.fromCallable(() -> {
    Thread.sleep(100);
    return "2_" + counter2.incrementAndGet();
}).subscribeOn(Schedulers.fromExecutor(executor1)));

如果您对代码执行此操作,则2_航行经过1_,尽管事实上它的睡眠时间是原来的100倍,但您开始看到的结果却与预期的结果更加接近:

...
2_17075
2_17076
1_863
1_864
2_17077
1_865
2_17078
2_17079
...

但是,请注意:

  

我想在发布者之间取得平衡(相对于线程池大小和处理时间)

您不能在这里选择数字来平衡输出,至少不能可靠地或以任何有意义的方式进行-线程调度将完全是任意的。如果要这样做,则可以使用this variant of the subscribe method,从而允许您在订阅使用者上显式调用request()。然后,您可以通过仅请求准备处理的元素来提供背压。