Hikari申请db连接的过程

HikariDataSource#getConnection → HikariPool#getConnection

public Connection getConnection(final long hardTimeout) throws SQLException
   {
      suspendResumeLock.acquire(); //获取信号量,这个是提供给jmx控制的功能 默认是空实现,可忽略
      final long startTime = currentTime();

      try {
         long timeout = hardTimeout;
         do {
						//从connectionBag获取连接,如果超时则直接退出
            PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
            if (poolEntry == null) {
               break; // We timed out... break and throw exception
            }

            final long now = currentTime();
						// 如果poolEntry被标记为evicted,则要关闭这个连接
						// 如果poolEntry上次访问时间到现在超过 aliveBypassWindowsMs=500ms,需要检测下连接是否可用,如果不可用的话,关闭连接
            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);
							 // 返回代理db连接
               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();
      }
   }

connectionBag.borrow() 的过程参见 Hikari定制连接池容器ConcurrentBag

成功获取到 poolEntry 后时,leakTaskFactory.schedule(poolEntry) 会启动一个连接泄露的检测任务 ProxyLeakTask ,如果超过 leakDetectionThreshold(默认是0,不开启) 时长还未归还则认为这个连接泄露了

 ProxyLeakTask schedule(final PoolEntry poolEntry)
 {
		// 默认是关闭的
    return (leakDetectionThreshold == 0) ? ProxyLeakTask.NO_LEAK : scheduleNewTask(poolEntry);
 }

 
 private ProxyLeakTask scheduleNewTask(PoolEntry poolEntry) {
    ProxyLeakTask task = new ProxyLeakTask(poolEntry);
	  //一次性任务,在leakDetectionThreshold后执行一次
    task.schedule(executorService, leakDetectionThreshold);

    return task;
 }

executorService使用的是 houseKeepingExecutorService 管家调度线程池, 在连接从池子里拿出 leakDetectionThreshold 时间后执行一次 ProxyLeakTask#run()

public void run()
   {
      isLeaked = true;

      final StackTraceElement[] stackTrace = exception.getStackTrace(); 
      final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 5];
      System.arraycopy(stackTrace, 5, trace, 0, trace.length);

      exception.setStackTrace(trace);
      LOGGER.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
   }

标识已经泄露了 isLeaked = true, 并打印 'Connection leak detection xxx' 警告日志

当连接 close() 归还回池子时,会取消这个 ProxyLeakTask#cancel()

void cancel()
   {
      scheduledFuture.cancel(false);
			// 如果是被标记为”泄露"后再取消的,则打印个日志说明下
      if (isLeaked) {
         LOGGER.info("Previously reported leaked connection {} on thread {} was returned to the pool (unleaked)", connectionName, threadName);
      }
   }
posted @ 2021-04-16 00:44  mushishi  阅读(659)  评论(0编辑  收藏  举报