如何在订阅中使用阻塞操作包装Flux?

时间:2018-08-29 06:55:32

标签: spring-webflux

在文档中写道,您应该将阻塞代码包装到Mono中:http://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking

但没有写出实际的操作方法。

我有以下代码:

@PostMapping(path = "some-path", consumes = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Mono<Void> doeSomething(@Valid @RequestBody Flux<Something> something) {
    something.subscribe(something -> {
        // some blocking operation
    });

    // how to return Mono<Void> here?
}

我在这里遇到的第一个问题是我需要退货,但我不能。 例如,如果我返回一个Mono.empty,则在完成磁通量操作之前,该请求将被关闭。

第二个问题是:我如何像文档中建议的那样实际包装阻塞代码:

Mono blockingWrapper = Mono.fromCallable(() -> { 
    return /* make a remote synchronous call */ 
});
blockingWrapper = blockingWrapper.subscribeOn(Schedulers.elastic()); 

1 个答案:

答案 0 :(得分:2)

您不应在控制器处理程序中调用subscribe,而应建立一个反应式管道并返回它。最终,HTTP客户端将(通过Spring WebFlux引擎)请求数据,这就是向管道订阅和请求数据的原因。

手动订阅将使请求处理与该其他操作分离,这将1)取消有关操作顺序的任何保证,并2)如果该其他操作正在使用HTTP资源(例如请求正文),则中断该处理。 / p>

在这种情况下,源没有被阻塞,而只有转换操作被阻塞。因此,我们最好使用publishOn来表示信号链的其余部分应在特定的Scheduler上执行。如果此处的操作受I / O限制,则Scheduler.elastic()是最佳选择,如果受CPU限制,则Scheduler.paralell更好。这是一个示例:

@PostMapping(path = "/some-path", consumes = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Mono<Void> doSomething(@Valid @RequestBody Flux<Something> something) {

    return something.collectList()
      .publishOn(Scheduler.elastic())
      .map(things -> { 
         return processThings(things);
      })
      .then();        
}

public ProcessingResult processThings(List<Something> things) {
  //...
}

有关该主题的更多信息,请查看Scheduler section in the reactor docs。如果您的应用程序倾向于做很多这样的事情,那么您将失去反应式流的许多好处,并且您可能会考虑切换到基于Servlet的模型,在该模型中可以相应地配置线程池。