Hikari 连接池关闭连接问题

Posted java知路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hikari 连接池关闭连接问题相关的知识,希望对你有一定的参考价值。

HIkari是java中最快的连接池,也是springboot默认的连接池。项目中使用hakari 连接池管理conn,在使用过程中遇到如果没有声明事物,连接不会关闭的情况,故花时间看了hakari的源码

另外如果要看具体问题与解决方法最好去github 上找一下。

 

HikariConfig 另外配置类中也做了详细说明,推荐看下这个类,其中作者标名了某些配置的默认值、执行过程中可改变与不可改变的值

 private static final char[] ID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30); private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5); private static final long IDLE_TIMEOUT = MINUTES.toMillis(10); private static final long MAX_LIFETIME = MINUTES.toMillis(30); private static final int DEFAULT_POOL_SIZE = 10;
private static boolean unitTest = false;
// Properties changeable at runtime through the HikariConfigMXBean // private volatile long connectionTimeout; private volatile long validationTimeout; private volatile long idleTimeout; private volatile long leakDetectionThreshold; private volatile long maxLifetime; private volatile int maxPoolSize; private volatile int minIdle; private volatile String username; private volatile String password;
// Properties NOT changeable at runtime // private long initializationFailTimeout; private String catalog; private String connectionInitSql; private String connectionTestQuery; private String dataSourceClassName; private String dataSourceJndiName; private String driverClassName; private String jdbcUrl; private String poolName; private String schema; private String transactionIsolationName; private boolean isAutoCommit; private boolean isReadOnly; private boolean isIsolateInternalQueries; private boolean isRegisterMbeans; private boolean isAllowPoolSuspension; private DataSource dataSource; private Properties dataSourceProperties; private ThreadFactory threadFactory; private ScheduledExecutorService scheduledExecutor; private MetricsTrackerFactory metricsTrackerFactory; private Object metricRegistry; private Object healthCheckRegistry; private Properties healthCheckProperties;

Hikari 连接池关闭连接问题

 

关于hikari 如何处理空闲连接的:

hikaripool类中有一个内部类,huosekeeper,它干的活就是维持空闲连接与消除过于的连接。下面是源码

它的工作机制就是,在程序启动后,datasource 交给 hikari 池代理,初始化空闲连接后(根据你的最小空闲连接配置决定),启动一个schedule,周期默认是30S

在调度任务执行的过程中会检查连接池包(currentBag)中连接的状态,如果是 NOT_IN_USER 的连接,那么将会进行释放。

如果当前空闲连接小于预定义的连接数,hikari会create 空闲连接,直接满足最小空闲连接要求(默认最小是10)


   private final class HouseKeeper implements Runnable{ private volatile long previous = plusMillis(currentTime(), -HOUSEKEEPING_PERIOD_MS);
@Override      public void run(){ try { // refresh timeouts in case they changed via MBean connectionTimeout = config.getConnectionTimeout(); validationTimeout = config.getValidationTimeout(); leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());
final long idleTimeout = config.getIdleTimeout(); final long now = currentTime();
// Detect retrograde time, allowing +128ms as per NTP spec. if (plusMillis(now, 128) < plusMillis(previous, HOUSEKEEPING_PERIOD_MS)) { LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.", poolName, elapsedDisplayString(previous, now)); previous = now; softEvictConnections(); return; } else if (now > plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) { // No point evicting for forward clock motion, this merely accelerates connection retirement anyway LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now)); }
previous = now;
String afterPrefix = "Pool "; if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) { logPoolState("Before cleanup "); afterPrefix = "After cleanup ";
final List<PoolEntry> notInUse = connectionBag.values(STATE_NOT_IN_USE); int toRemove = notInUse.size() - config.getMinimumIdle(); for (PoolEntry entry : notInUse) { if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) { closeConnection(entry, "(connection has passed idleTimeout)"); toRemove--; } } }
logPoolState(afterPrefix);
fillPool(); // Try to maintain minimum connections } catch (Exception e) { LOGGER.error("Unexpected exception in housekeeping task", e); } } }


Hikari 连接池关闭连接问题

那么重点就在于NOT_IN_USE 状态什么时候设置的,只有一个地方会直接把IN_USER 设置为NOT_IN_USER

 

 

 

在currentBag 中,那么什么时候会调用requite 方法,当conn 关闭的时候,通过hikari 代理最终close 会执行到requite 方法,这时候hikari 会把当前连接池中的连接,

也就是下图中的bagEntry 设置为NOT_IN_USE, 设置完毕后,具体清理工作交给housekeeper 处理。

另外当我们使用conn 的时候,hikari 同样会获取连接池中的当前空闲连接交给你使用。

 

 

 

而我遇到的问题是,当调用一个自定义的service 方法,并且其中有操作数据库的操作(个人调用的是一个oracle 存储过程),由于spring jpa 事物管理中对于自定义的数据库操作并没有加上

我定义的方法相关配置,于是在调用存储过程后没有调用close 方法,故hikari 不会设置激活状态下的连接为空闲状态,随着时间的推移,最终导致连接池中的数量达到了maxpoolsize,无法再次获取连接导致connecttimeout异常。

 

最后一点是:遇到hikari 的问题,最好把hikari的log日志打开(设置debug),这样默认情况下每30S ,hikari 的housekeeper 会打印当前池中的连接情况,包括连接总数,激活数,空闲数与等待数量。

<Logger name="com.zaxxer.hikari" level="debug"></Logger>

 

另外这个问题解决方法就是让conn 每次使用完后管理连接,从而通知hikari 代理把当前使用的连接设置为空闲连接进行回收。

在方法上加上声明式事物注解,即可解决。

   @Override
@Transactional(rollbackFor = Exception.class)
public void callLogStatistic(String inBeginTime, String inSiteId) {
repository.logStatistic(inBeginTime, inSiteId);
}

 


以上是关于Hikari 连接池关闭连接问题的主要内容,如果未能解决你的问题,请参考以下文章

hikari连接池的logintimeout设置过大会造成啥后果

聊聊hikari连接池的leakDetectionThreshold

聊聊hikari连接池的isAllowPoolSuspension

hikari为什么不需要配置连接池初始化大小

Hikari连接池

Hikari 连接池 postgres