[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.
定位到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);
}
}
...
...
...
}
定位到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();
}
}
...
...
...
}
从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;
}
}
目前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
按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