如何并行处理Flux事件?

时间:2019-04-28 16:24:39

标签: project-reactor

我有一些传入事件流,需要对其进行充实,然后在它们到达时并行处理。

我当时以为Project Reactor是按订单生产的,但是在我的测试中,所有处理似乎都是按顺序进行的。

以下是一些测试代码:

ExecutorService executor = Executors.newFixedThreadPool(10);
System.out.println("Main thread: " + Thread.currentThread());
Flux<String> tick = Flux.interval(Duration.of(10, ChronoUnit.MILLIS))
        .map(i-> {
            System.out.println("ReactorTests.test " + Thread.currentThread());
            sleep(1000L); // simulate IO delay
            return String.format("String %d", i);
        })
        .take(3)
//    .subscribeOn(Schedulers.elastic());
//    .subscribeOn(Schedulers.newParallel("test"));
//    .subscribeOn(Schedulers.fromExecutor(executor));
;
tick.subscribe(x ->System.out.println("Subscribe thread: " + Thread.currentThread()), 
               System.out::println, 
               ()-> System.out.println("Done"));
System.out.println("DONE AND DONE");

我尝试取消注释的每一行的注释,但是在每种情况下,输出都表明使用同一线程来处理所有事件

Main thread: Thread[main,5,main]
[DEBUG] (main) Using Console logging
DONE AND DONE
ReactorTests.test Thread[parallel-1,5,main]
Subscribe thread: Thread[parallel-1,5,main]
ReactorTests.test Thread[parallel-1,5,main]
Subscribe thread: Thread[parallel-1,5,main]
ReactorTests.test Thread[parallel-1,5,main]
Subscribe thread: Thread[parallel-1,5,main]
Done

(唯一的区别是,没有调度程序,它们在预订线程上运行,而对于任何执行程序,它们都在同一线程上运行,而不是在订阅线程上。)

我想念什么?

仅供参考,有一种“睡眠”方法:

public static void sleep(long time) {
    try {
        Thread.sleep(time);
    } catch (InterruptedException e) {
        System.out.println("Exiting");
    }
}

1 个答案:

答案 0 :(得分:1)

并行处理项目的一种方法是使用.parallel / .runOn

flux
    .parallel(10)
    .runOn(scheduler)
    //
    // Work to be performed in parallel goes here.  (e.g. .map, .flatMap, etc)
    //
    // Then, if/when you're ready to go back to sequential, call .sequential()
    .sequential()

阻塞操作(例如阻塞IO或Thread.sleep)将阻塞执行它们的线程。反应流无法神奇地将阻止方法转变为非阻止方法。因此,您需要确保在适合于阻塞操作的Scheduler上运行阻塞方法(例如Schedulers.elastic())。

在上面的示例中,由于您知道要调用阻塞操作,因此可以使用.runOn(Schedulers.elastic())

根据使用情况,您还可以将.flatMap之类的异步运算符与.subscribeOn.publishOn结合使用,将特定的阻塞操作委托给另一个Scheduler,例如{ {3}}。例如:

flux
    .flatMap(i -> Mono.fromCallable(() -> {
            System.out.println("ReactorTests.test " + Thread.currentThread());
            sleep(1000L); // simulate IO delay
            return String.format("String %d", i);
        })
        .subscribeOn(Schedulers.elastic()))

实际上,.flatMap也有一个重载的变体,它带有一个concurrency参数,您可以在其中限制飞行中内部序列的最大数量。在某些用例中,可以使用它代替.parallel。不过,由于Flux.interval不支持下游请求的补充速度比滴答声慢,因此它通常不适用于Flux.interval