在SpringBoot中使用R2DBC连接池的源码和教程


随着微服务架构的兴起,反应式应用程序变得越来越流行。为了充分利用反应式系统的潜力,建议使我们所有的系统都具有反应性。
但是,在做出充分反应的应用仍然在JVM世界相当大的挑战,因为JDBC(Java数据库连接)是同步的,并封锁连接到关系数据库,其中大部分应用程序用来存储数据的API。
为了解决这个问题,Pivotal(Spring 框架背后的公司)带领社区努力创建一种连接到数据库的异步方式,这个项目现在称为 R2DBC(反应性关系数据库连接)。
在 R2DBC 倡议之后,Spring 团队决定在 Spring 生态系统中支持 R2DBC,因此Spring Data R2DBC诞生了。
 
项目源码点击标题
 
项目设置
必须包含三个依赖项:

  1. Spring Data R2DBC
  2. PostgreSQL 驱动程序
  3. Spring 响应式 Web

 
持久层
public interface MemberRepository extends R2dbcRepository<Member, Long> {
  Mono<Member> findByName(String name);
}

在这个 R2DBC 存储库中,自定义方法不是Member直接返回,而是包含在Mono. 此存储库中的每个方法都将使用Mono或Flux。这与Project Reactor使用的内容一致,因此我们与反应式 API 完全兼容。
不仅如此R2dbcRepository,在 Spring Data R2DBC 中,我们还可以选择使用以下两个选项之一扩展接口:
  1. ReactiveCrudRepository, 用于您的通用 CRUD 存储库和,
  2. ReactiveSortingRepository 用于额外的排序功能。

您可以选择这些选项中的任何一个,具体取决于您的用例。

控制器:

@RestController
@RequestMapping(value = "/api/member")
@RequiredArgsConstructor
public class MemberController {

  private final MemberRepository memberRepository;

  @GetMapping
  public Flux<Member> getAll() {
    return memberRepository.findAll();
  }

  @GetMapping(value = "/{name}")
  public Mono<Member> getOne(@PathVariable String name) {
    return memberRepository.findByName(name);
  }
}

Spring Data R2DBC 已经包含连接池选项,我们只需从我们的properties中启用它。
对于此示例,我们将创建 2 个单独的端点,它们将使用Mono和Flux:

  1. 在method上getAll,我们使用之前创建的repository来获取数据库中的所有数据,因为我们会得到1个以上的数据,返回的数据被包裹在Flux.
  2. 在 method 上getOne,我们请求一条数据,在这种情况下,我们将返回的数据包装在Mono.

我们现在差不多完成了。现在我们已经设置了 API,我们需要做的就是将应用程序连接到我们的数据库。
  
R2BC连接池配置
HikariCP连接池在R2BC中不可用,连接池选择使用R2DBC Pool
您可以将以下属性添加到您的application.properties文件中,R2DBC 将能够将连接集中到我们的数据库。

spring.r2dbc.url=r2dbc:postgresql://postgres@localhost:5432/reactive
logging.level.org.springframework.r2dbc=DEBUG

spring.r2dbc.pool.enabled=true
spring.r2dbc.pool.initial-size=50
spring.r2dbc.pool.max-size=100

这些属性将启动具有 50 个可供使用的连接的池,并且在需要时可以扩展到最多 100 个连接。
有多种方法可以对我们的应用程序进行负载测试,您甚至可以通过ab命令直接从终端使用 Apache Benchmark 。
可以使用一个名为K6的工具:
测试一个 API,它将向我们的数据库中插入一百条记录。这将是对数据库施加压力的大量写入操作。
如果没有连接池,我们只能完成 140 个请求,第 95 个百分位数为 2.33s。
而使用R2DBC的连接池,我们完成了 480 次请求迭代,将吞吐量提高了近 3.5 倍,并将第 95 个百分点的延迟降低到 668 毫秒,这几乎是延迟的 4 倍!
 
关于池的大小:
池大小最终非常特定于部署。
例如,混合了长时间运行的事务和非常短的事务的系统通常最难使用任何连接池进行调整。在这些情况下,创建两个池实例可以很好地工作(例如,一个用于长时间运行的作业,另一个用于“实时”查询)。
在主要具有长时间运行事务的系统中,所需连接数通常存在“外部”约束——例如,作业执行队列只允许一次运行一定数量的作业。在这些情况下,作业队列的大小应该是“合适的”以匹配池(而不是相反)。