使用R2DBC实现数据库的响应式访问

栏目: Java · 发布时间: 7年前

内容简介:Reactive Programming可以看作是一种编程模型,它通过创建事件驱动的非阻塞功能管道来促进可扩展性和稳定性,这些管道对资源的可用性和可处理性做出反应。延迟执行,只有当整个堆栈都是被动的并且所有参与的组件(应用程序代码,运行时容器,集成)都遵循延迟执行,非阻塞API和数据流的流式特性时,响应式编程的全部好处才会生效 - 基本上遵循基本假设。虽然可以将非反应性组件引入以函数响应式编写的应用程序,但最终结果是对可扩展性和稳定性影响,实际预期收益减少。在最坏的情况下,运行时行为很少或没有区别。但是,

Reactive Programming可以看作是一种编程模型,它通过创建事件驱动的非阻塞功能管道来促进可扩展性和稳定性,这些管道对资源的可用性和可处理性做出反应。延迟执行, 并发和异步性 只是底层编程模型的结果。

只有当整个堆栈都是被动的并且所有参与的组件(应用程序代码,运行时容器,集成)都遵循延迟执行,非阻塞API和数据流的流式特性时,响应式编程的全部好处才会生效 - 基本上遵循基本假设。

虽然可以将非反应性组件引入以函数响应式编写的应用程序,但最终结果是对可扩展性和稳定性影响,实际预期收益减少。在最坏的情况下,运行时行为很少或没有区别。但是,响应式编程有助于提高代码的可读性。

如果我们查看响应式生态系统,我们将发现几个框架,库和集成。他们每个人都有自己的特长。通过通用方法或在特定响应框架的上下文中,可以很好地涵盖许多功能领域。我们来讨论关系数据库集成。

最常见的问题是:我们什么时候可以使用API​​进行响应性关系数据库集成?

Java使用JDBC作为与关系数据库集成的主要技术。JDBC具有阻塞性 - 没有什么可以减轻JDBC的阻塞性质。关于如何使调用无阻塞的第一个想法是将JDBC调用卸载到Executor(通常是Thread池)。虽然这种方法有些作用,但它有几个缺点,忽略了响应式编程模型的好处。

响应运行时通常使用与CPU核心数匹配的有限数量的线程。额外的线程引入开销并减少线程限制的影响。此外,JDBC调用通常会堆积在队列中,一旦线程充满请求,池将再次阻塞。所以,JDBC现在不是唯一一个选择。

响应式数据库API

Oracle宣布推出 ADBA ,该计划旨在通过使用期货为 Java 中的异步数据库访问提供标准化API。Postgres正在研究可用于第一次实验的 Postgres ADBA驱动程序PgNio 是Postgres的另一个异步驱动程序,它开始尝试使用ADBA。

ADBA的可用性未知。它绝对不会在Java 12的版本中可使用,ADBA计划首次亮相的Java版本目前尚不清楚。

以下代码段显示了使用INSERT和SELECT语句的ADBA :

DataSource ds = dataSource();
CompletableFuture<Long> t;

try (Session session = ds.getSession()) {

  Submission<Long> submit = session
  .<Long>rowCountOperation(
    "INSERT INTO legoset (id, name, manual) " +
    "VALUES($1, $2, $3)")
    .set("$1", 42055, AdbaType.INTEGER)
    .set("$2", "Description", AdbaType.VARCHAR)
    .set("$3", null, AdbaType.INTEGER)
    .apply(Result.RowCount::getCount)
    .submit();

  t = submit.getCompletionStage().toCompletableFuture();
}

t.join();

CompletableFuture<List<Map<String, Object>>> t;
try (Session session = ds.getSession()) {

  Submission<List<Map<String, Object>>> submit = session
    .<List<Map<String, Object>>> rowOperation(
      "SELECT id, name, manual FROM legoset")
    .collect(collectToMap()) // custom collector
    .submit();
  t = submit.getCompletionStage().toCompletableFuture();
}

t.join();

请注意,这collectToMap(…)是应用程序提供的函数的示例,该函数将结果提取到所需的返回类型。

R2DBC

由于缺乏标准API和驱动程序不可用, Pivotal 的团队开始研究反应性关系API的想法,该API非常适合用于反应式编程。他们提出了 R2DBC ,它代表了Reactive Relational Database Connectivity。截至目前,R2DBC是一个孵化器项目,用于评估可行性并开始讨论驱动程序供应商是否有兴趣支持反应/非阻塞/异步驱动程序。

截至目前,有三种驱动程序实现:

R2DBC附带API规范(r2dbc-spi)和客户端(r2dbc-client),使SPI可用于应用程序。

以下代码段显示R2DBC SPI使用INSERT和SELECT语句:

ConnectionFactory connectionFactory = null;

Mono<Integer> count = Mono.from(connectionFactory.create())
  .flatMapMany(it ->
    it.createStatement(
    "INSERT INTO legoset (id, name, manual) " +
    "VALUES($1, $2, $3)")
      .bind("$1", 42055)
      .bind("$2", "Description")
      .bindNull("$3", Integer.class)
      .execute())
  .flatMap(io.r2dbc.spi.Result::getRowsUpdated)
  .next();

Flux<Map<String, Object>> rows = Mono.from(connectionFactory.create())
  .flatMapMany(it -> it.createStatement(
    "SELECT id, name, manual FROM legoset").execute())
  .flatMap(it -> it.map((row, rowMetadata) -> collectToMap(row, rowMetadata)));

虽然上面的代码有点笨重,但R2DBC还附带了一个客户端库项目,用于更人性化的用户API。R2DBC SPI不用于直接使用,而是通过客户端库使用。

使用R2DBC客户端重写的相同代码将是:

R2dbc r2dbc = new R2dbc(connectionFactory);

Flux<Integer> count = r2dbc.inTransaction(handle ->
  handle.createQuery(
    "INSERT INTO legoset (id, name, manual) " +
    "VALUES($1, $2, $3)")
    .bind("$1", 42055)
    .bind("$2", "Description")
    .bindNull("$3", Integer.class)
    .mapResult(io.r2dbc.spi.Result::getRowsUpdated));

Flux<Map<String, Object>> rows = r2dbc
  .inTransaction(handle -> handle.select(
    "SELECT id, name, manual FROM legoset")
  .mapRow((row, rowMetadata) -> collectToMap(row, rowMetadata));

请注意,collectToMap(…)作为应用程序提供的函数的示例,该函数将结果提取到所需的返回类型。

Spring Data团队将 Spring Data R2DBC 作为孵化器启动,通过数据库客户端提供响应API并支持反应式存储库。使用Spring Data R2DBC重写的示例代码将是:

DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);

Mono<Integer> count = databaseClient.execute()
  .sql(
    "INSERT INTO legoset (id, name, manual) " +
    "VALUES($1, $2, $3)")
  .bind("$1", 42055)
  .bind("$2", "Description")
  .bindNull("$3", Integer.class)
  .fetch()
  .rowsUpdated();

Flux<Map<String, Object>> rows = databaseClient.execute()
  .sql("SELECT id, name, manual FROM legoset")
  .fetch()
  .all();

R2DBC及其生态系统仍然很年轻,并要求进行实验和反馈以收集用例,并查看反应性关系数据库集成是否有意义。

Fibers的JDBC

虽然JDBC和其他技术暴露了阻塞API(主要是由于等待I / O),但 Project Loom 引入Fibers了轻量级抽象,将阻塞API转变为非阻塞API。一旦调用遇到阻塞API,就可以通过堆栈切换实现这一点。因此Fiber,继续使用阻塞API的先前流程的基础尝试。

该Fiber执行模式大大减少了所需的原生线程数。结果是更好的可伸缩性和非阻塞行为 - 通过卸载对Fiber-backed的阻塞调用Executor。我们所需要的只是一个适当的API,允许在Fiber实现上使用非阻塞JDBC 。

结论

反应式编程和关系数据库的未来是什么?老实说,我不知道。如果我尝试了一个有根据的猜测,我可以看到Project Loom和它的基于Fiber的Executor与成熟的JDBC驱动程序相结合,成为业界潜在的游戏规则改变者。随着Java的加速发布节奏,这可能并不遥远。

ADBA的目标是包含在Java标准版运行时中,根据当前的时间表,我预计它将在早于2021年的Java 17中出现。

与现在可用的R2DBC形成对比。它配备了驱动程序和客户端,并允许实验使用。R2DBC的一个简洁的副作用是它暴露完全反应的API,同时独立于底层数据库引擎。随着已经发布的版本,没有必要猜测Project Loom,也不需要等待三年才能测试其驱动API。今天就可以使用R2DBC。


以上所述就是小编给大家介绍的《使用R2DBC实现数据库的响应式访问》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Twenty Lectures on Algorithmic Game Theory

Twenty Lectures on Algorithmic Game Theory

Tim Roughgarden / Cambridge University Press / 2016-8-31 / USD 34.99

Computer science and economics have engaged in a lively interaction over the past fifteen years, resulting in the new field of algorithmic game theory. Many problems that are central to modern compute......一起来看看 《Twenty Lectures on Algorithmic Game Theory》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具