基于Java连接池Druid的应用开发

本文介绍基于JDBC连接池Druid连接并访问Lindorm宽表引擎的使用方法。

前提条件

  • 已安装Java环境,要求安装JDK 1.8及以上版本。

  • 已配置白名单,具体操作,请参见设置白名单

  • 宽表引擎的版本为2.3.1及以上版本,如何升级版本,请参见升级小版本

注意事项

  • Lindorm前端接入节点使用SLB负载均衡,客户端连接前端节点。为了将客户端请求较为均匀地发送到各个前端节点,建议连接保持的时间不宜太长,可以配置phyMaxUseCountphyTimeoutMillis参数。

  • 执行查询前从连接池获取连接,执行完查询后要及时调用conn.close()将连接返回到连接池中。再次使用时,请从连接池中获取新连接,避免长时间使用一个连接导致Druid无法及时检查到连接失效。

  • 在复杂网络环境下,若遇到网关性能达到瓶颈、网络链路长、网络抖动、重传率或丢包率高等情况,可能会导致连接中断。建议确保连接池配置合理,并在必要时通过业务代码侧的重试机制优化。

  • 服务端升级重启时,连接可能会短暂中断。即使使用了连接池,业务侧仍可能感知到异常。建议捕获异常并重试。

  • 根据业务情况合理调整连接池配置,并确保配置生效。您可以在程序中通过DruidDataSource#getStatData()DruidDataSource#dump()方法定期获取生效的配置与连接池的信息,并在日志中查看、核对相关配置信息。

准备工作

  • 通过连接池Druid连接Lindorm宽表引擎前,需要安装连接池DruidLindorm JDBC Driver。以Maven项目为例,在pom.xml文件的dependencies中添加以下依赖项。

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.11</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun.lindorm</groupId>
      <artifactId>lindorm-all-client</artifactId>
      <version>2.2.1.3</version>
    </dependency>
  • 当通过druid-spring-boot-starter使用Druid连接池时,需要先排除druid-spring-boot-starter依赖的Druid组件,然后显式依赖Druid组件,如下所示。

    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid-spring-boot-starter</artifactId>
       <version>1.2.11</version>
       <exclusions>
          <exclusion>
             <groupId>com.alibaba</groupId>
             <artifactId>druid</artifactId>
          </exclusion>
       </exclusions>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.11</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun.lindorm</groupId>
      <artifactId>lindorm-all-client</artifactId>
      <version>2.2.1.3</version>
    </dependency>

操作步骤

说明

您可以直接下载示例代码在本地编译和运行,也可以使用示例代码开发您的项目代码。

  1. 配置连接池Druid的参数。在Maven项目的src/main/resources目录中新建druid.properties文件,并在文件中添加以下内容。

    # 驱动类名,无需替换
    driverClassName=com.aliyun.lindorm.table.client.Driver
    # url、username、password需要替换为业务实际的内容,可以在Lindorm控制台上获取
    url=jdbc:lindorm:table:url=http://ld-bp17j28j2y7pm****.lindorm.rds.aliyuncs.com:30060
    username=****
    password=****
    # 连接属性,指定要连接的database,需根据实际情况将****替换为实际的内容
    connectionProperties=database=****
    
    # 初始化连接池即创建连接,建议保持不变
    init=true
    # 初始化连接池时建立连接的个数,可以根据实际情况调整
    initialSize=10
    # 连接池中维护的空闲连接的数量,可以根据实际情况调整。对性能要求高的场景建议和maxActive的值相同;如果业务存在明显的峰谷波动,可以设置小一些
    minIdle=40
    # 连接池中允许的最大连接数量,可以根据实际情况调整,建议与业务线程池大小相同
    maxActive=40
    # 获取连接最大等待时间,单位毫秒,建议保持不变
    maxWait=30000
    
    # 配置一个连接最大使用次数,避免长时间使用相同连接造成服务器端负载不均衡,对性能略有影响
    druid.phyMaxUseCount=10000
    
    # 连接保活配置项,建议保持不变,否则可能出现连接断开
    # 异常ConnectionDisconnectedException
    druid.keepAlive=true
    druid.keepAliveBetweenTimeMillis=30000
    minEvictableIdleTimeMillis=300000
    maxEvictableIdleTimeMillis=600000
    timeBetweenEvictionRunsMillis=5000
    phyTimeoutMillis=1800000
    
    # 连接验证配置项,建议保持不变
    validationQuery=SELECT 1
    testWhileIdle=true
    testOnBorrow=false
    testOnReturn=false
    
    # PreparedStatement缓存配置项,这里配置为关闭缓存,建议保持不变
    # 否则运行时可能会出现NoSuchStatement异常
    poolPreparedStatements=false
    maxOpenPreparedStatements=-1
    druid.maxPoolPreparedStatementPerConnectionSize=-1
    说明
    • 请根据注释说明替换或者调整以下配置项:

      • url:连接宽表引擎的连接地址,获取方法请参见获取连接信息

      • username:访问宽表引擎的用户名,通过Lindorm宽表引擎的集群管理系统查看,具体请参见管理用户

      • password:访问宽表引擎的密码。如果您忘记了密码,可以通过Lindorm宽表引擎的集群管理系统修改密码,具体操作请参见修改用户密码

    • 更多配置项的说明请参见DruidDataSource配置属性列表

  2. 加载连接池Druid的参数并初始化连接池Druid。

    // 加载参数
    Properties properties = new Properties();
    InputStream inputStream = DruidPoolDemo.class.getClassLoader().getResourceAsStream("druid.properties");
    properties.load(inputStream);
    // 初始化连接池
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
  3. 通过连接池Druid获取JDBC的连接信息并访问Lindorm宽表引擎。

    /* -------------- 基于JDBC的访问示例 ----------------- */
    
    String tableName = "sql_table_" + new Random().nextInt(1000);
    // 创建表
    try (Connection connection = dataSource.getConnection()) {
        try (Statement statement = connection.createStatement()) {
            String sql = "create table if not exists " + tableName + "(id VARCHAR, name VARCHAR, primary key(id))";
            int ret = statement.executeUpdate(sql);
            System.out.println(ret);
        }
    }
    
    // 插入数据
    try (Connection connection = dataSource.getConnection()) {
        String sql = "upsert into " + tableName + "(id,name) values(?,?)";
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setString(1, "aa");
            ps.setString(2, "bb");
    
            int ret = ps.executeUpdate();
            System.out.println(ret);
        }
    }
    
    // 查询数据
    try (Connection connection = dataSource.getConnection()) {
        String sql = "select * from " + tableName + " where id=?";
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setString(1, "aa");
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                String id = rs.getString(1);
                String name = rs.getString(2);
                System.out.println("id=" + id);
                System.out.println("name=" + name);
            }
        }
    }
    
    // 删除数据
    try (Connection connection = dataSource.getConnection()) {
        String sql = "delete from " + tableName + " where id=?";
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setString(1, "aa");
            ps.executeUpdate();
        }
    }

附录:连接池负载均衡说明

image

连接池模式(TCP长连接)的效率更高,但在以下场景中对分布式负载均衡不友好,可能导致连接负载不均匀:

  • 突发创建大量连接,导致分布不均

    当应用突发创建了大量的连接时,若负载均衡设备未能及时刷新后端节点的连接统计信息,可能会导致部分后端LDServer节点承载较多的连接请求。同时,叠加连接池化机制,最终会导致部分LDServer节点压力高于其他节点,连接分布不均,影响系统总体性能。

  • 负载均衡探活异常,导致分布不均

    负载均衡通过主动探活来判断后端节点是否正常,当探活出现偶发异常时,可能会导致部分LDServer节点上连接较少,结合连接池化,最终会导致部分LDServer压力低于其他节点,影响系统总体性能。

Druid连接池增加了phyTimeoutMillisphyMaxUseCount参数,定期(例如每执行30分钟或者10,000次)刷新连接池中的连接,可以在解决上述问题的同时保持性能基本不变,建议默认添加这两个参数。