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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM