HikariPool鏈接池初始化

HikariPool 鏈接池在初始化的時候主要作了幾件事:java

  • 初始化底層的鏈接容器 ConcurrentBag
  • checkFailFast() 嘗試建立一個db鏈接,若是失敗則直接拋出初始化異常 中斷初始化
  • 初始化各種資源
public HikariPool(final HikariConfig config)
   {
      super(config);
      // 1. 構建自定義的線程池容器 ConcurrentBag
      this.connectionBag = new ConcurrentBag<>(this);
      this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;

      this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
		
      // 2. 建立鏈接,校驗有效性 快速失敗
      checkFailFast();
      
      // 若是傳入opentracing的 metricsTracker就能夠上報鏈接池的一些指標信息
      if (config.getMetricsTrackerFactory() != null) {
         setMetricsTrackerFactory(config.getMetricsTrackerFactory());
      }
      else {
         setMetricRegistry(config.getMetricRegistry());
      }

      setHealthCheckRegistry(config.getHealthCheckRegistry());

      handleMBeans(this, true);

      ThreadFactory threadFactory = config.getThreadFactory();

      final int maxPoolSize = config.getMaximumPoolSize();
			// 3. 鏈接新增線程池的 blockQueue,上限爲 maxPoolSize
      LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize);
      // 4. addConnectionQueueReadOnlyView 用來查看當前有幾個db鏈接任務
      this.addConnectionQueueReadOnlyView = unmodifiableCollection(addConnectionQueue);
      // 5. 構建負責新建db鏈接的線程池,1個工做線程、隊列上限maxPoolSize
      this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
      // 6. 構建負責關閉db鏈接的線程池,1個工做線程、隊列上限maxPoolSize
      this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize, poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

			// 7. 鏈接泄露檢測(leakDetectionThreshold默認爲0,不開啓),db鏈接從池子中取出後開始計時,若是超過必定的時長還未歸還則認爲可能發現鏈接泄露/慢查詢了
      this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
			// 8. houseKeepingExecutor 定時任務,用來維護鏈接池內 minimumIdle個鏈接
			// 間隔30ms檢查一次鏈接池,若是不足 minimumIlde則填充,超過minimumIlde~maxPoolSize的那部分鏈接若是空閒時長 > idleTimeout則關閉掉
      this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(**new HouseKeeper()**, 100L, housekeepingPeriodMs, MILLISECONDS);

			// 若是系統變量設置的阻塞等待 鏈接池填充完畢,則會等待 houseKeeperTask 把鏈接池填充完畢 
     if (Boolean.getBoolean("com.zaxxer.hikari.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
				 // 給負責新建db鏈接的線程池 加大火力
         addConnectionExecutor.setCorePoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));
         addConnectionExecutor.setMaximumPoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));

         final long startTime = currentTime();
         while (elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) {
            quietlySleep(MILLISECONDS.toMillis(100));
         }
				
         addConnectionExecutor.setCorePoolSize(1);
         addConnectionExecutor.setMaximumPoolSize(1);
      }
   }

幾個關鍵對象:ide

  • addConnectionExecutor : 負責新建db鏈接的線程池 1個工做線程、隊列上限maxPoolSize
  • addConnectionQueueReadOnlyView : 是 addConnectionExecutor 的阻塞隊列視圖,用來獲取當前有幾個db鏈接等待建立,避免提交過多新建任務致使超過max
  • closeConnectionExecutor: 負責關閉db鏈接的線程池 1個工做線程、隊列上限maxPoolSize
  • leakTaskFactory : db鏈接泄露任務工廠,在db鏈接申請成功後建立鏈接泄露檢查任務,若是超過必定時長爲歸還 則認爲鏈接泄露了(沒有close 或者慢查詢) 默認 leakDetectionThreshold = 0 不開啓
  • houseKeeperTask : 」大管家「定時任務,定時檢查鏈接池內鏈接數,維持到 minimumIdle 的數量 HouseKeeper

HouseKeeper#run() 爲了維持鏈接池的鏈接數量穩定在 minimumIdle 個

  1. 池內空閒的鏈接, 超過 minimumIdle 的那部分,若是空閒超過 idleTimeout 則清除掉
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--;
      }
   }
}
  1. 調用 fillPool(); 空閒db鏈接數補足到 minimumIdle 個
private synchronized void fillPool()
{
   final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections()) - addConnectionQueueReadOnlyView.size(); //這時候 addConnectionExecutor 的隊列視圖就有用了
   if (connectionsToAdd <= 0) logger.debug("{} - Fill pool skipped, pool is at sufficient level.", poolName);

   for (int i = 0; i < connectionsToAdd; i++) {
      addConnectionExecutor.submit((i < connectionsToAdd - 1) ? poolEntryCreator : postFillPoolEntryCreator);
   }
}
  • 待填充的db鏈接個數 = Math.min( 「最大鏈接數 - 總鏈接數」, 「最小空閒鏈接數 - 當前空閒鏈接數」) - 等待新建db鏈接的鏈接數
    這邊會扣除正在進行的鏈接新建任務數 addConnectionQueueReadOnlyView.size() 避免重複新增
  • 提交給 addConnectionExecutor 單線程池 新建鏈接
相關文章
相關標籤/搜索