概述

在这个教程中,我们将学习如何使用 FlexyPoolSpring Boot 和 H2 配合使用 HikariCP 进行数据库连接池管理。它是一个建立在主流连接池之上的强大工具。

什么是FlexyPool?

现代Web应用中的一个重要方面是连接池管理。它确保多个客户端共享数据库连接,从而实现更快、更高效的数据库访问。

然而,管理连接池是一项复杂且具有挑战性的任务。当客户端数量增加和应用复杂性提高时,这一点尤为明显。这时,FlexyPool 就派上用场了。

FlexyPool 是一个强大的连接池管理工具,它简化了数据库连接的管理并优化性能。简单来说,它作为 Hikari、C3P0、DBCP2、Tomcat 和 Vibur 等主要连接池的代理。为了达到目标,库提供了指标和故障转移策略,可以根据需要动态调整给定池的大小:

2.1. FlexyPool 属性

FlexyPool 提供了两个重要属性:

  • connectionAcquireTimeThresholdMillis:定义连接获取请求的时间阈值。超过这个时间后,将发布 ConnectionAcquireTimeThresholdExceededEvent
  • connectionLeaseTimeThresholdMillis:这是连接可以租赁的最大时间,然后返回到池中。当池超过这个阈值时,会发布 ConnectionLeaseTimeThresholdExceededEvent

2.2. FlexyPool 策略

FlexyPool 提供两种策略来应对连接池获取失败的情况。

第一个策略是 IncrementPoolOnTimeoutConnectionAcquiringStrategy。在这种策略下,如果在连接获取过程中超时,FlexyPool 将增加目标连接池的最大大小。该策略有两个选项:maxOverflowPoolSizetimeoutMillis

maxOverflowPoolSize 设置目标连接池可以扩展到的最大限制。

timeoutMillis 设置在尝试池大小增加之前等待的时间,其默认值为连接池的超时值。

策略 RetryConnectionAcquiringStrategy 指令 FlexyPool 在尝试从池中获取连接并重试 retryAttempts 次后放弃。retryAttempts 表示尝试的重试次数。

我们将在 FlexyPoolDataSource 的配置中设置这些策略。

安装

首先,让我们安装 HikariCP 连接池:

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.1.0</version>
</dependency>

接下来,添加 FlexyPool 的依赖项HikariCP 配置适配器Micrometer 适配器

<dependency>
    <groupId>com.vladmihalcea.flexy-pool</groupId>
    <artifactId>flexy-hikaricp</artifactId>
    <version>2.2.3</version>
    <exclusions>
        <exclusion>
            <groupId>com.vladmihalcea.flexy-pool</groupId>
            <artifactId>flexy-dropwizard-metrics</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.vladmihalcea.flexy-pool</groupId>
    <artifactId>flexy-micrometer-metrics</artifactId>
    <version>2.2.3</version>
</dependency>

由于我们使用 Micrometer 作为度量实现,因此默认排除了 Dropwizard-Metrics。此外,FlexyPool 对其他支持的连接池框架也有 安装指南。如果你使用的 Java 版本低于 1.8,这也非常有用。

配置

要使 FlexyPool 起作用,我们首先需要一个 FlexyPoolDataSource 数据源。这需要针对所使用的连接池(如 HikariDataSource)的特定数据源。现在,让我们设置它们。

4.1. HikariDataSource 配置

首先,我们使用内存中的 H2 数据库配置 Hikari 数据源:

@Bean
public HikariDataSource hikariDataSource() {

    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=runscript from 'classpath:/db.sql'");
    config.setUsername("");
    config.setPassword("");
    config.addDataSourceProperty("cachePrepStmts", "true");
    config.addDataSourceProperty("prepStmtCacheSize", "250");
    config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    config.addDataSourceProperty("minimumPoolSize", "1");
    config.addDataSourceProperty("maximumPoolSize", "3");
    config.addDataSourceProperty("connectionTimeout", "1000");
    return new HikariDataSource(config);
}

在这里,我们添加了 maximumPoolSizeconnectionTimeout 属性。正如我们之前看到的,这些属性对 FlexyPool 的 IncrementPoolOnTimeoutConnectionAcquiringStrategy 有帮助。

接下来,我们通过 HikariDataSource 配置一个bean:

@Bean
public Configuration<HikariDataSource> flexypoolConfiguration() {
    HikariDataSource dataSource = hikariDataSource();
    return new Configuration.Builder<>(UUID.randomUUID().toString(), dataSource, HikariCPPoolAdapter.FACTORY)
      .setMetricsFactory(MicrometerMetrics::new)
      .setConnectionProxyFactory(ConnectionDecoratorFactoryResolver.INSTANCE.resolve())
      .setMetricLogReporterMillis(TimeUnit.SECONDS.toMillis(5))
      .setMetricNamingUniqueName(UniqueNamingStrategy.INSTANCE)
      .setJmxEnabled(true)
      .setJmxAutoStart(true)
      .setConnectionAcquireTimeThresholdMillis(50L)
      .setConnectionLeaseTimeThresholdMillis(250L)
      .setEventListenerResolver(() -> Arrays.asList(
        new ConnectionAcquireTimeoutEventListener(), 
        new ConnectionAcquireTimeThresholdExceededEventListener(), 
        new ConnectionLeaseTimeThresholdExceededEventListener()))
      .build();
}

这个配置:

  • 启用了 JMX 报告
  • 设置度量实现为 Micrometer。Micrometer 适配器默认使用 SimpleMetricsRegistry。但是,它可以自定义以使用集成监控报告工具,如 Ganglia 或 Graphite。
  • 向各种连接事件添加监听器ConnectionAcquireTimeoutEventConnectionAcquireTimeThresholdExceededEventConnectionLeaseTimeThresholdExceeded。值得注意的是,这些监听器是同步的,不应执行耗时任务。
  • 设置连接租赁时间为 250ms
  • 设置连接获取时间为 50ms

4.2. FlexyPoolDataSource 配置

现在,让我们配置一个 FlexyPoolDataSource bean:

@Bean(initMethod = "start", destroyMethod = "stop")
public FlexyPoolDataSource<HikariDataSource> flexypoolDataSource() {
    Configuration<HikariDataSource> configuration = flexypoolConfiguration();
    return new FlexyPoolDataSource<>(
      configuration, 
      new IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory<>(5), 
      new RetryConnectionAcquiringStrategy.Factory<>(2));
}

这个 FlexyPoolDataSource 在连接超时时向 Hikari 池添加额外的五个连接。如果池无法获取连接,它还会最多尝试两次重试连接。

最后,我们可以通过调用 FlexyPoolDataSource 运行我们的应用程序:

@SpringBootApplication
public class FlexypoolDemoApplication {

    private static FlexyPoolDataSource<HikariDataSource> poolDataSource;

    public FlexypoolDemoApplication(FlexyPoolDataSource<HikariDataSource> poolDataSource) {
        FlexypoolDemoApplication.poolDataSource = poolDataSource;
    }

    public static List<Employee> getEmployees() throws SQLException {
        String SQL_QUERY = "select * from emp";
        List<Employee> employees;
        try (Connection con = poolDataSource.getConnection(); PreparedStatement pst = con.prepareStatement(SQL_QUERY); ResultSet rs = pst.executeQuery();) {
            employees = new ArrayList<>();
            Employee employee;
            while (rs.next()) {
                employee = new Employee();
                employee.setEmpNo(rs.getInt("empno"));
                employee.setEname(rs.getString("ename"));
                employee.setJob(rs.getString("job"));
                employee.setMgr(rs.getInt("mgr"));
                employee.setHiredate(rs.getDate("hiredate"));
                employee.setSal(rs.getInt("sal"));
                employee.setComm(rs.getInt("comm"));
                employee.setDeptno(rs.getInt("deptno"));
                employees.add(employee);
            }
        }
        return employees;
    }

    public static void main(String[] args) throws SQLException {
        SpringApplication.run(FlexypoolDemoApplication.class, args);
        List<Employee> employees = getEmployees();
        System.out.println(employees);
    }
}

总结

在这篇文章中,我们了解了如何在 Spring Boot 和 H2 使用 HikariCP 连接池时使用 FlexyPool。

首先,我们了解了如何设置和配置 FlexyPool,以及如何监控其指标。

如往常一样,完整的源代码可以在 GitHub 上找到