在Spring Webflux中执行阻塞的JDBC调用

时间:2019-02-25 10:26:59

标签: java spring-boot spring-webflux project-reactor

我正在使用Spring Webflux和Spring Data jpa,并使用PostgreSql作为后端数据库。 我不想在进行findsave之类的数据库调用时阻塞主线程。 为此,我在Controller类和jdbcScheduler服务类中都有一个主调度程序。

我定义它们的方式是:

@Configuration
@EnableJpaAuditing
public class CommonConfig {

    @Value("${spring.datasource.hikari.maximum-pool-size}")
    int connectionPoolSize;

    @Bean
    public Scheduler scheduler() {
        return Schedulers.parallel();
    }

    @Bean
    public Scheduler jdbcScheduler() {
        return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize));
    }

    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }
}

现在,在服务层中进行获取/保存调用时,我会这样做:

    @Override
    public Mono<Config> getConfigByKey(String key) {
        return Mono.defer(
            () -> Mono.justOrEmpty(configRepository.findByKey(key)))
            .subscribeOn(jdbcScheduler)
            .publishOn(scheduler);
    }

    @Override
    public Flux<Config> getAllConfigsAfterAppVersion(int appVersion) {
        return Flux
            .fromIterable(configRepository.findAllByMinAppVersionIsGreaterThanEqual(appVersion))
            .subscribeOn(jdbcScheduler)
            .publishOn(scheduler);
    }

    @Override
    public Flux<Config> addConfigs(List<Config> configList) {
        return Flux.fromIterable(configRepository.saveAll(configList))
            .subscribeOn(jdbcScheduler)
            .publishOn(scheduler);
    }

在控制器中,我这样做:

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    Mono<ResponseDto<List<Config>>> addConfigs(@Valid @RequestBody List<Config> configs) {
        return configService.addConfigs(configs).collectList()
            .map(configList -> new ResponseDto<>(HttpStatus.CREATED.value(), configList, null))
            .subscribeOn(scheduler);
    }

这是正确的吗?和/或有更好的方法吗?

我的理解是:

.subscribeOn(jdbcScheduler)
.publishOn(scheduler);

是该任务将在jdbcScheduler线程上运行,以后的结果将在我的主并行scheduler上发布。这种理解正确吗?

2 个答案:

答案 0 :(得分:2)

您对enumpublishOnsee reference documentation in the reactor project about those operators)的理解是正确的。

如果您在没有调度的情况下调用阻止库而不在特定调度程序上工作,则这些调用将阻止少数可用线程之一(默认情况下为Netty事件循环),并且您的应用程序将只能同时处理几个请求。

现在我不确定您要这样做是什么。

首先是parallel scheduler is designed for CPU bound tasks,这意味着您将拥有很少的CPU核心,数量更多(或更多)。在这种情况下,就像将您的线程池大小设置为常规Servlet容器上的内核数一样。您的应用将无法处理大量并发请求。

即使您选择了更好的选择(如弹性Scheduler),它也仍然不如Netty事件循环好,后者是在Spring WebFlux中本地调度请求处理的地方。

如果您的最终目标是性能和可伸缩性,则将阻塞调用包装在响应式应用程序中的性能可能会比常规Servlet容器差。

您可以改为使用Spring MVC和:

    在处理诸如JPA之类的阻塞库时,
  • 使用常规的阻塞返回类型
  • 当您不绑定此类库时,请使用subscribeOnMono返回类型

这不会是非阻塞的,但这仍然是异步的,您将能够并行执行更多工作,而无需处理复杂性。

答案 1 :(得分:0)

恕我直言,有一种方法可以更好地利用机器资源来执行此操作。按照文档,您可以在其他线程中wrap the call 并通过此文档继续执行。