解决HikariCP连接池提示TDengine ERROR (8000000b)问题

解决HikariCP连接池提示TDengine ERROR (8000000b)问题

  • 一、异常堆栈日志
  • 二、查看HikariCP源码
    • 2.1 CodahaleHealthChecker类
    • 2.2 HikariPool类
    • 2.3 分析静态代码块
  • 三、翻阅TDengine官方文档
    • 3.1 参考TDengine与连接池HikariCP的使用[^1]
    • 3.2 查看应用的yaml配置
  • 四、更新max-lifetime
  • 五、观察应用运行状态

一、异常堆栈日志

[xxx-xxx-xxx/v1.0.0 - 192.168.0.9:8080]2022-10-0802:00:09.086WARN 1[][http-nio-6666-exec-8]
com.zaxxer.hikari.pool.PoolBaseTDengineHikariCP - Failed to validate connection com.taosdata.jdbc.TSDBConnection@13788ad1 
(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.

二、查看HikariCP源码

2.1 CodahaleHealthChecker类

定位到CodahaleHealthChecker类确认异常发生位置,在check函数中执行健康检查pool.getConnection(checkTimeoutMs)

public final class CodahaleHealthChecker{
...
...
...
      @Override
      protected Result check() throws Exception
      {
         try (Connection connection = pool.getConnection(checkTimeoutMs)) {
            return Result.healthy();
         }
         catch (SQLException e) {
            return Result.unhealthy(e);
         }
      }
...
...
...
}

2.2 HikariPool类

定位到getConnection函数,看到是属于HikariPool类的函数,其内部实现了isConnectionAlive(poolEntry.connection)的判断

public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateListener
{
...
...
...
   public Connection getConnection(final long hardTimeout) throws SQLException
   {
      suspendResumeLock.acquire();
      final long startTime = currentTime();

      try {
         long timeout = hardTimeout;
         do {
            PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
            if (poolEntry == null) {
               break; // We timed out... break and throw exception
            }

            final long now = currentTime();
            if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
               closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
               timeout = hardTimeout - elapsedMillis(startTime);
            }
            else {
               metricsTracker.recordBorrowStats(poolEntry, startTime);
               return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
            }
         } while (timeout > 0L);

         metricsTracker.recordBorrowTimeoutStats(startTime);
         throw createTimeoutException(startTime);
      }
      catch (InterruptedException e) {
         Thread.currentThread().interrupt();
         throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
      }
      finally {
         suspendResumeLock.release();
      }
   }
...
...
...
}

2.3 分析静态代码块

isConnectionAlive(poolEntry.connection)的实现看到,大概是因为调用connection.createStatement()发生异常触发了catch代码块的执行,抛出:(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.

   boolean isConnectionAlive(final Connection connection)
   {
      try {
         try {
            setNetworkTimeout(connection, validationTimeout);

            final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;

            if (isUseJdbc4Validation) {
               return connection.isValid(validationSeconds);
            }

            try (Statement statement = connection.createStatement()) {
               if (isNetworkTimeoutSupported != TRUE) {
                  setQueryTimeout(statement, validationSeconds);
               }

               statement.execute(config.getConnectionTestQuery());
            }
         }
         finally {
            setNetworkTimeout(connection, networkTimeout);

            if (isIsolateInternalQueries && !isAutoCommit) {
               connection.rollback();
            }
         }

         return true;
      }
      catch (Exception e) {
         lastConnectionFailure.set(e);
         logger.warn("{} - Failed to validate connection {} ({}). Possibly consider using a shorter maxLifetime value.",
                     poolName, connection, e.getMessage());
         return false;
      }
   }

三、翻阅TDengine官方文档

3.1 参考TDengine与连接池HikariCP的使用[^1]

解决HikariCP连接池提示TDengine ERROR (8000000b)问题_第1张图片

3.2 查看应用的yaml配置

目前max-lifetime设置了1800000(单位ms)即30min,此时会抛出Failed to validate connection的异常

#数据源配置
spring:
  datasource:
    tdengine:
      driver-class-name: com.taosdata.jdbc.TSDBDriver
      jdbc-url: jdbc:TAOS://xxx-hostname:6030/xxx?enableMicrosecond=true&charset=UTF-8&locale=en_US.UTF-8
      username: xxx
      password: xxx
      ...
      ...
      ...
      #连接池名
      pool-name: TDengineHikariCP
      #最大生命周期
      max-lifetime: 1800000
      #连接超时时间
      connection-timeout: 30000
      connection-test-query: select 1 

四、更新max-lifetime

按TDengine官方示例,将max-lifetime设置为0,表示最大生命周期。这里正确的做法应该是逐步调小max-lifetime的数值,直到不再抛出异常,参考Github HikariCP issues[^2]

#数据源配置
spring:
  datasource:
    tdengine:
      ...
      #最大生命周期
      max-lifetime: 0
      ...

五、观察应用运行状态

检查是否仍抛出异常(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.

参考内容:
[1]:TDengine与连接池HikariCP的使用 https://docs.taosdata.com/2.4/reference/connector/java/#hikaricp
[2]:逐步调小max-lifetime参数值 https://github.com/brettwooldridge/HikariCP/issues/1651

你可能感兴趣的:(java,TDengine,Hikari,tdengine,java)