Joe*_*oey 5 java reactive-programming project-reactor spring-webflux
我开始在 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;
});
}
Run Code Online (Sandbox Code Playgroud)
现在是的,我知道有一种简单的方法可以被动地做到这一点。这不是我要问的问题(实际上,真正的代码要复杂得多)。
我真正想知道的是同步数据库调用会对我的性能产生什么影响。整个方法是否仍然是异步非阻塞的(除了在进行 db 调用的部分,这是阻塞的)?或者这会以某种方式破坏整个反应式范式并导致整个事情,开始到结束,阻塞?
您需要了解反应式的症结所在,并且您将能够自己回答这个问题。反应式中的每个操作员一次处理一个项目。每个操作员还有一个队列,如果操作员很忙,可以将项目排队。现在考虑以下代码:
Flux.fromIterable(Arrays.asList(1,2,3,4,5))
.flatMap(a -> {
Thread.sleep(2000);
return Mono.just(a);
}).subscribe(System.out::println);
Run Code Online (Sandbox Code Playgroud)
您会看到项目以 2 秒的间隔打印。这是因为flatMap的线程被阻塞了 2 秒,并且元素的其余项都在排队。flatMap的线程如果想从其队列中消费项目,就必须变得空闲。
在您的情况下,您说数据库调用正在阻塞。这将导致呼叫thread被阻塞。所以你的方法既不是non-blocking也不是asynchronous。但是,做类似的事情
Mono.just(whatever)
.subscribeOn(Schedulers.elastic())
.flatMap(whatever -> returnThingRepo.getByWhateverId(whatever.ID));
Run Code Online (Sandbox Code Playgroud)
在flatMapand里面你的方法会变成non-blockingand asynchronous。发生这种情况是因为一旦调用.subscribeOn()操作符,调用线程就会空闲。
希望这回答了你的问题。
这并没有打破反应式范式。在我看来,反应式不仅仅是非阻塞的异步操作。除了NIO.
要记住的黄金法则是,您永远不能使阻塞方法成为非阻塞方法。你可以简单地反过来,你可以做各种各样的事情来不完全破坏反应式范式,但是没有办法将它本质上变成异步。
或者这会以某种方式破坏整个反应式范式并导致整个事情,开始到结束,阻塞?
不幸的是,比这更糟糕。假设它不会使应用程序崩溃,它将导致少数反应性线程之一(或者可能是单个反应性线程)在您的阻塞数据库调用执行时阻塞。这意味着需要使用该线程的所有其他反应性操作(这很可能是您的整个应用程序)将必须等到阻塞的数据库调用完成才能调度它们,这对性能是一个关键的影响。
处理此类情况(您需要从反应链进行阻塞调用)的公认方法是使用bounded elastic scheduler,它将执行委托给后端线程池,以免占用主事件线程( s)。