如果进行单个同步调用,Webflux 会发生什么?

What happens in Webflux if a single synchronous call is made?

我正在 Spring Boot 中使用 Webflux 涉足反应式 Java 编程世界。

我 运行 遇到这样一种情况,在这种情况下,很难被动地执行某个数据库调用。

如果我在 Mono 中执行单个阻塞数据库调用,会发生什么情况?

代码看起来像这样...

public Mono<ReturnThing> isThisAsyncOrNot() {
    //Async, non-blocking API call
    return webClient.doSomeAPIWork()
                     .flatMap(whatevers -> {
                         //Synchronous, blocking database call
                         ReturnThing returnThing= returnThingRepo.getByWhateverId(whatever.ID);
                         }
                         return returnThing;
                     });
}

现在是的,我知道有一种简单的方法可以被动地执行此操作。这不是我要问的问题(事实上,真正的代码要复杂得多)。

我真正想知道的是同步数据库调用对我的表现有何影响。整个方法是否仍然是异步非阻塞的(除了在进行 db 调用的部分,它是阻塞的)?还是这会以某种方式破坏整个反应式范式并导致整个事情从头到尾阻塞?

要记住的黄金法则是永远不能使阻塞方法成为非阻塞方法。你可以采取另一种方式,你可以做各种事情来不完全破坏反应式范式,但是没有办法将它变成内在的异步。

Or will this somehow ruin the whole reactive paradigm and cause the entire thing, start to finish, to be blocking?

不幸的是,比这更糟。假设它不会使应用程序崩溃,它将导致少数反应线程之一(或者可能是 单个 反应线程)在阻塞数据库调用执行时阻塞。这意味着所有其他需要使用该线程的反应性操作(这很可能是您的整个应用程序)将必须等到阻塞数据库调用完成才能安排它们,这对性能是一个严重的打击。

处理这类情况(您需要从反应链进行阻塞调用)的公认方法是使用 bounded elastic scheduler,它将执行委托给后端线程池,因此以免占用主事件线程。

你需要了解反应式的症结,你才能自己回答这个问题。反应式中的每个操作员一次处理一个项目。每个操作员还有一个队列,如果操作员忙,可以将项目排队。现在考虑以下代码:

Flux.fromIterable(Arrays.asList(1,2,3,4,5))
    .flatMap(a -> {
         Thread.sleep(2000);
         return Mono.just(a);
    }).subscribe(System.out::println);

您会看到项目以 2 秒的间隔打印。这是因为 flatMap 的线程被阻塞了 2 秒,其余元素的项目都在排队。 flatMap 的线程必须空闲才能使用其队列中的项目。

在你的例子中,你说数据库调用被阻塞了。这将导致调用 thread 被阻止。所以你的方法既不会是 non-blocking 也不会是 asynchronous。但是,做一些像

Mono.just(whatever)
    .subscribeOn(Schedulers.elastic())
    .flatMap(whatever -> returnThingRepo.getByWhateverId(whatever.ID));

flatMap 中,您的方法将变为 non-blockingasynchronous。发生这种情况是因为一旦调用 .subscribeOn() 运算符,调用线程就会空闲。

希望这能回答您的问题。

这并没有打破反应式范式。在我看来,响应式不仅仅是非阻塞的异步操作。除了 NIO.

之外,惯用性和简单的多线程功能对我来说是一个很大的卖点