分享

Spring-5-webflux和阻塞与非阻塞JDBC

 太极混元天尊 2018-05-24

原文链接:https:///articles/spring-5-webflux-and-jdbc-to-block-or-not-to-block

作者:Grygoriy Gonchar

译者:developlee

期待良久的Spring5提供了更多对反应式的支持,接下来我们来研究一下如何使JDBC更加异步,以及这么做带来的问题

Spring Framework 5 为函数式响应型编程带来了巨大的进步,你不需要使用ApplicationContext或几十个注释来启动和运行最简单的REST API。Spring 5将提供轻量级Web功能和被动的Web Flux支持,以帮助实现这种转换。这些新特性使得Java和spring 5 更适合构建反应式的web 应用。

为了实现反应式web应用,根据反应式声明 (The Reactive Manifesto)所说,应用必须是响应式,弹性的,可伸缩的,并且是消息驱动的。该列表中的最后一个条件——消息驱动,导致了异步通信方式的大迁移,包括异步RPC和消息传递库、数据库驱动程序等等。关系型数据库(RDBMS)是非常强大和有用的。数据库供应商提供的JVM数据库访问的官方工具是实现JDBC API的驱动程序。但是JDBC被设计成阻塞并且每次数据库的调用都是使用线程的。你无法在API本身的方法或接口中找到能在另一个线程中获得查询结果的接口。
还有
一种观点认为,事物型数据库不适合反应性概念:

事务的概念并不适用于反应性的世界,因为它是关于阻塞资源的,而这正是你想要避免的。

我部分同意这种观点。然而,这取决于你如何正确使用关系数据库。此外,在高度可伸缩的SQL数据库的新时代(如Amazon的Aurora或谷歌的Cloud Spanner),通过将它们一起使用来实现高吞吐量是合适的。问题是,当我们在一个SQL数据库上构建反应式应用程序时,我们应该做什么?
一种选择是使用完全非阻塞的替代SQL客户机。比如这里和这里。
当然,这些驱动程序还没有得到数据库厂商的官方支持。此外,与成熟的基于jdbc的抽象(如Hibernate或jOOQ)相比,功能的吸引力要小得多。

另一种想法是我从Scala中找到的。我们可以将阻塞调用分发到一个单独的线程池,以避免将阻塞和非阻塞调用混合在一起。这么做允许我们控制线程的总数量,并允许CPU在主执行上下文中提供非阻塞任务,并应用各种优化。
假设我们有一个基于jdbc的实现,比如Spring Data JPA,它确实是阻塞的。

import org.springframework.data.repository.CrudRepository;public interface AddressRepository extends CrudRepositoryAddress, Long> {}

我们可以让它异步执行,并分发给专用的线程池。

@Servicepublic class AddressService {    private final AddressRepository repository;    private final Scheduler scheduler;    public AddressRouter(AddressRepository repository, @Qualifier('jdbcScheduler') Scheduler scheduler) {        this.repository = repository;        this.scheduler = scheduler;    }    public Mono<>
> findAll() {        return async(() -> repository.findAll());    }    private Mono async(Callable callable) {        return Mono.fromCallable(callable).publishOn(scheduler);    }}

我们的JDBC调度程序应该使用一个专用的线程池来配置,它的大小等于连接的数量。

@Configurationpublic class SchedulerConfiguration {    private final Integer connectionPoolSize;    public SchedulerConfiguration(@Value('${spring.datasource.maximum-pool-size}') Integer connectionPoolSize) {        this.connectionPoolSize = connectionPoolSize;    }    @Bean    public Scheduler jdbcScheduler() {        return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize));    }}

然而,这种方法也有困难。主要是事务管理。在JDBC中,事务只能在一个java.sql.Connection中进行。要想在一个事务中进行多个操作,它们必须共享一个连接。如果想在两者之间进行一些运算,则必须让它们保持联系。这么做的话效率不是很高,因为在他们运算的过程中保持了相对数量的闲置连接。

异步JDBC包装器的概念并不新鲜,并且已经在Scala Slick 3库中实现了。最后,非阻塞JDBC可能会出现在Java路线图上。正如它在2016年9月在JavaOne上宣布的那样,我们可能会在Java 10中看到它。


推荐:原始性能表格 - Spring Boot 2 Webflux vs. Spring Boot 1

上一篇:




最好的赞赏

就是你的关注


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多