Skip to content

整合agroal ​

本文介绍如何在 tio-boot 项目中使用 Agroal 连接池 作为数据源,并与 jOOQ 集成,同时提供事务管理能力。

该方案具有以下特点:

  • 无 Spring 依赖
  • 程序化配置(对 AOT / Native 更友好)
  • 启动路径清晰
  • 易于控制生命周期
  • 性能稳定

适用于:

  • 高性能 REST / RPC 服务
  • AOT / GraalVM 方向项目
  • 不希望引入重量级框架的系统

一、依赖配置 ​

添加 Agroal 连接池:

xml
<dependency>
    <groupId>io.agroal</groupId>
    <artifactId>agroal-pool</artifactId>
    <version>3.0</version>
</dependency>

同时确保已引入:

  • PostgreSQL Driver(或的数据库驱动)
  • jOOQ

例如:

xml
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.5.0</version>>
</dependency>

二、整体架构说明 ​

启动时完成以下初始化流程:

  1. 读取数据库配置
  2. 构建 Agroal ConnectionFactory
  3. 配置连接池参数
  4. 创建 DataSource
  5. 初始化 jOOQ DSLContext
  6. 注册事务管理器
  7. 注入 AOP 容器
  8. 在 JVM 关闭时优雅释放连接池

三、完整配置示例 ​

java
package demo.jooq.config;

import java.sql.SQLException;
import java.time.Duration;

import javax.sql.DataSource;

import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;

import nexus.io.annotation.AConfiguration;
import nexus.io.annotation.Initialization;
import nexus.io.constants.ServerConfigKeys;
import nexus.io.hook.HookCan;
import nexus.io.jfinal.aop.AopManager;
import nexus.io.tio.utils.environment.EnvUtils;

import demo.jooq.tx.TransactionManager;
import io.agroal.api.AgroalDataSource;
import io.agroal.api.configuration.supplier.AgroalConnectionFactoryConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalConnectionPoolConfigurationSupplier;
import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier;
import io.agroal.api.security.NamePrincipal;
import io.agroal.api.security.SimplePassword;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AConfiguration
public class JooqConfig {

  @Initialization
  public void initJooq() throws SQLException {

    // 读取数据库配置
    String jdbcUrl = EnvUtils.get(ServerConfigKeys.JDBC_URL);
    String username = EnvUtils.getStr(ServerConfigKeys.JDBC_USER);
    String password = EnvUtils.getStr(ServerConfigKeys.JDBC_PSWD);

    /**
     * 1. 连接工厂配置
     * 明确指定 Driver,有助于 AOT / Native 编译
     */
    AgroalConnectionFactoryConfigurationSupplier connectionFactory =
        new AgroalConnectionFactoryConfigurationSupplier()
            .connectionProviderClass(org.postgresql.Driver.class)
            .jdbcUrl(jdbcUrl)
            .principal(new NamePrincipal(username))
            .credential(new SimplePassword(password));

    /**
     * 2. 连接池配置
     */
    AgroalConnectionPoolConfigurationSupplier poolConfig =
        new AgroalConnectionPoolConfigurationSupplier()
            .connectionFactoryConfiguration(connectionFactory)
            .minSize(0)
            .maxSize(20)
            .acquisitionTimeout(Duration.ofSeconds(30))
            .maxLifetime(Duration.ofMinutes(30));

    /**
     * 3. 创建 DataSource
     */
    AgroalDataSourceConfigurationSupplier dsConfig =
        new AgroalDataSourceConfigurationSupplier()
            .connectionPoolConfiguration(poolConfig)
            .metricsEnabled(true);

    AgroalDataSource agroalDataSource = AgroalDataSource.from(dsConfig);

    /**
     * 4. 初始化 jOOQ
     */
    DSLContext dslContext =
        DSL.using((DataSource) agroalDataSource, SQLDialect.POSTGRES);

    /**
     * 5. 注册单例
     */
    AopManager.me().addSingletonObject(AgroalDataSource.class, agroalDataSource);
    AopManager.me().addSingletonObject(DSLContext.class, dslContext);

    /**
     * 6. 注册事务管理器
     */
    TransactionManager txManager =
        new TransactionManager(agroalDataSource, SQLDialect.POSTGRES);

    AopManager.me().addSingletonObject(TransactionManager.class, txManager);

    log.info("jOOQ + Agroal initialized");

    /**
     * 7. 优雅关闭连接池
     */
    HookCan.me().addDestroyMethod(() -> {
      try {
        agroalDataSource.close();
        log.info("AgroalDataSource closed");
      } catch (Exception e) {
        log.warn("Failed to close AgroalDataSource", e);
      }
    });
  }
}

四、关键设计说明 ​

1. 为什么推荐程序化配置? ​

优点:

  • 避免反射
  • 避免 XML / SPI 扫描
  • 对 GraalVM 更友好
  • 初始化路径可控

如果未来考虑:

  • Native Image
  • Serverless
  • 极速冷启动

这种方式非常推荐。


2. 为什么要显式指定 Driver? ​

强烈建议:

java
.connectionProviderClass(org.postgresql.Driver.class)

原因:

在 AOT / Native 编译中:

  • DriverManager 自动发现可能失效
  • ServiceLoader 可能被裁剪

显式声明最安全。


3. minSize = 0 是否合理? ​

推荐策略:

中低流量系统 ​

minSize = 0

降低空闲连接成本。

高频系统 ​

minSize = CPU核心数 或 10+

减少冷连接延迟。


4. maxSize 如何设置? ​

一个常见经验公式:

maxSize ≈ (CPU核心数 * 2) + 有效磁盘数

但更重要的是:

不要超过数据库允许连接数。 ​

例如:

PostgreSQL 默认:

max_connections = 100

的应用池最好:

10 ~ 30

而不是 80。


五、Agroal vs 常见连接池 ​

推荐程度:

如果目标是 AOT / Native: ​

首选:

Agroal

如果只追求极致 micro-benchmark: ​

HikariCP

但在真实业务中:

SQL、锁、网络远比连接池更影响性能。

差距通常小于 2%。


六、常见问题 ​

Q:Agroal 性能如何? ​

非常高。

在现代连接池中属于第一梯队。

通常瓶颈不会在连接池。


Q:是否适合高并发? ​

完全适合。

Quarkus 默认连接池就是它。


Q:是否支持 Native? ​

是的,而且设计目标之一就是支持云原生运行环境。


七、建议的生产配置 ​

推荐起步值:

minSize = 2~5
maxSize = 20~40
acquisitionTimeout = 5~10秒
maxLifetime = 20~30分钟

再根据:

  • TPS
  • SQL耗时
  • DB CPU

逐步压测调整。


总结 ​

该方案实现了:

  • tio-boot + jOOQ
  • 高性能连接池
  • 可控生命周期
  • 对 AOT 友好
  • 无框架绑定

MIT Licensed