关于reactor项目:Spring webflux:flatmap异步改造

Spring webflux: flatmap asynchronous transformation

我读过平面图转换是异步的,在这个例子中,我在 lambda 定义中打印线程的名称。它正在打印订阅源的同一线程。根据我的理解,它应该打印一个不同的线程名称 - 除了订阅源之外,因为这个转换必须在不同的线程中执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
Flux.just(1, -2, 3, 4, -5, 6)
    .flatMap(element -> {
        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() +" element:" + element);
        return Flux.just(element);
    })
    .subscribe()

它是异步的这一事实并不一定意味着它是并行运行的,这似乎是您在这里所期望的。但是,您可以将 Flux 转换为 ParallelFlux 并指定并行调度程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Flux.just(1, -2, 3, 4, -5, 6)
        .parallel()
        .runOn(Schedulers.elastic())
        .flatMap(element
                -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) { // TODO Auto-generated catch block
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()
                    +" element:" + element);

            return Flux.just(element);
        })
        .subscribe();
Thread.currentThread().join(); //Just a hack to keep the program alive.

另一方面,如果您不希望它并行运行,而只是在主线程的单独线程上运行,则无需将其转换为并行 Flux - 只需提供一个 打电话或类似的代替。


不,它不会打印不同的线程名称。默认情况下,响应式在单个线程上运行 - 订阅发生在该线程上。在这种情况下,订阅发生在调用线程上,因此,所有元素都在其上发出并在其上进一步处理。

让我们首先尝试了解 .flatMap 的工作原理:

  • 对于每个上游事件,您可以从中创建一个流。
  • flatMap 热切地订阅每个内部流(这就是它们甚至被触发的原因)。请注意,这里的热切意味着它一次订阅所有内部流,而无需等待其他内部流完成(与 concatMap 不同)。
  • 动态合并所有内部流,因为它们发出事件。
  • 请注意,每个内部流毕竟是一个流。除非你告诉它使用不同的线程,否则每个线程都会在同一个线程上发出(调用线程,在你的情况下发生了什么)。如果您想并行处理它们,您可以为它们中的每一个执行 .subscribeOn(Schedulers.elastic)(或 Schedulers.parallel()):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Flux.just(1, -2, 3, 4, -5, 6)
        .flatMap(
            element -> {
              //this will always print the same thread - the calling thread basically
              System.out.println(Thread.currentThread().getName() +" element:" + element);
              return Mono.just(element)
                  .subscribeOn(Schedulers.parallel())
                  .doOnNext(
                      a ->
                          System.out.println(
                              a +" emitted on thread:" + Thread.currentThread().getName()));
            })
        .subscribe();

    .flatMap 不关心元素进入哪个线程,以及它退出哪个线程 - 它所要做的就是订阅内部流并在它们到来时合并它们的事件。请记住,每个内部流都是一个"Promise"。它最终会完成(带有 onComplete)信号,但你不知道什么时候。 flatMap 仍然不在乎。然而,concatMap 在订阅下一个流之前等待一个流完成。这就是它保持原始流顺序的方式。

    在此处阅读更多信息(我自己关于 flatMap 的文章):

    https://medium.com/swlh/understanding-reactors-flatmap-operator-a6a7e62d3e95


    flatMap 的使用不会影响它执行的线程。您可以使用 subscribeOn 来影响将发生执行的线程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
            Flux.just(1, -2, 3, 4, -5, 6)
                    .flatMap(element ->
                    {
                        try { Thread.sleep(1000);
                        } catch (InterruptedException e) { // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                        System.out.println(Thread.currentThread().getName() +
                               " element:" + element);
                        return Flux.just(element);
                    })
                    .subscribeOn(Schedulers.elastic())
                    .subscribe();

    根据您想要的行为,您可以使用以下任何一种 -
    Schedulers.elastic()Schedulers.single()Schedulers.parallel()Schedulers.immeadiate()