• Hikari 连接池关闭连接


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

    首先,hikari有一堆配置,这个配置的注意事项可以去网上找一下,这里提供一个地址 https://blog.51cto.com/1197822/2298344

    另外如果要看具体问题与解决方法最好去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 如何处理空闲连接的:

    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);
             }
          }
       }

    那么重点就在于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);
        }
  • 相关阅读:
    这样的程序员创业有戏
    一个29岁总裁对大学生的16条忠告
    向C#的String类添加按字节截取字符串的扩展方法
    妙用SQL Server聚合函数和子查询迭代求和
    为什么要在定义抽象类时使用abstract关键字
    C# 抽象类
    C# 虚函数和重载函数
    在指定文本里记录内容
    静态和非静态方法
    抽象类
  • 原文地址:https://www.cnblogs.com/jony-it/p/12859473.html
Copyright © 2020-2023  润新知