(2)Druid數據庫連接池如何獲取Connection原理和源碼分析?

(1)獲取連接方法getConnectionDirect()線程:

這裏是Druid的三個核心線程的交互邏輯圖
在這裏插入圖片描述
⚠️這裏是init();初始化在這一步:主要核心就是創建這幾個線程

createAndLogThread();  //打印日誌線程其實就是統計監控信息
createAndStartCreatorThread(); //創建連接的線程
createAndStartDestroyThread(); //銷燬連接的線程
  • 首先第一大步驟就是先去獲取DruidPooledConnection,通過getConnectionInternal()方法

    • 進入到方法內:

      1. 先進行一個for死循環當createDirect = true時跳出循環,首次爲false,所以下一步重要的就是創建ScheduledThreadPoolExecutor 對象,條件就是當:【poolingCount(池子中的值爲0時) && 活躍的連接 < maxActive && createScheduler != null && createScheduler instanceof ScheduledThreadPoolExecutor】 , 然後ScheduledThreadPoolExecutor中的getQueue().size() > 0 時,把createDirect = true,然後繼續往下走,
      2. 然後通過我們設置的setMaxWait 時間進行判斷,如果MaxWait > 0 ,就會從LRU的隊列中的尾部取出一個connection使用方法 pollLast(nanos); 否則走takeLast();
      3. 如果DruidConnectionHolder 不爲null ,把【connection】活躍的ActiveCount++,然後跳出循環
      4. 此時的createDirect = true,所以會去創建一個物理連接 賦值給 DruidConnectionHolder對象
      PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
      holder = new DruidConnectionHolder(this, pyConnInfo);
      
      1. 然後加鎖,判斷 activeCount < maxActive ,如果爲True就把活躍的Connection連接進行➕1,跳出循環,如果 activeCount > maxActive,則需要丟棄創建好的這個創建好的物理連接
      JdbcUtils.close(pyConnInfo.getPhysicalConnection());
      
    • 如果成功繼續走到下一步

    • 如果失敗捕獲住錯誤的話,就繼續嘗試獲取DruidPooledConnection

  • 然後回到getConnectionDirect()方法中,走到第二個步驟中,先判斷配置參數testOnBorrow是不是爲True,然後去進行對連接進行校驗,如果校驗成功就繼續往下走,

    boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
    if (!validate) {
        Connection realConnection = poolableConnection.conn;
        discardConnection(realConnection);
        continue;
    }
    
  • 如果配置參數testOnBorrow爲True的話會先進行一次上面代碼的校驗如果testOnBorrow爲False的話,並且 testWhileIdle參數爲True的話,會再進行判斷,如果【timeBetweenEvictionRunsMillis <= 0】直接使用 60s 進行填充

  • 繼續走,如果當前連接的空閒時間idleMillis >= timeBetweenEvictionRunsMillis || idleMillis < 0】時,會繼續進行校驗當前連接的健康情況,和上面代碼一樣,如果校驗不成功就會discard當前的Connection

  • 繼續,然後判斷配置參數removeAbandoned是不是爲True,如果是就以當前連接poolableConnection 爲Key,PRESENT作爲Value放到activeConnections的Map集合中

  Map<DruidPooledConnection, Object> activeConnections                         = new IdentityHashMap<DruidPooledConnection, Object>();
//這裏是判斷邏輯
            if (removeAbandoned) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.connectStackTrace = stackTrace;
                poolableConnection.setConnectedTimeNano();
                poolableConnection.traceEnable = true;

                activeConnectionLock.lock();
                try {
                    activeConnections.put(poolableConnection, PRESENT);
                } finally {
                    activeConnectionLock.unlock();
                }
            }
  • 繼續,如果配置的defaultAutoCommit參數是不是爲False,如果爲False,執行,poolableConnection.setAutoCommit(false);
  • 最後 getConnectionDirect(long maxWaitMillis)方法 返回DruidPooledConnection 對象 poolableConnection

⚠️這裏補充一下第一大步的第2小步中的pollLast(nanos)takeLast(nanos)方法中如果沒有從LRU隊列中的尾部獲取到Connection時,就會發送圖中的notify信號,去通知創建連接的線程去創建連接**

建議對照源碼進行分析:

1. 先進入到getConnection方法中

    public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        init();

        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }
  1. 然後進入到getConnectionDirect()方法中:
    public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;
        for (;;) {
            // handle notFullTimeoutRetry
            DruidPooledConnection poolableConnection;
            try {
            //這裏的getConnectionInternal方法,其實對應的就是上面分析中的第一大步裏面的邏輯
                poolableConnection = getConnectionInternal(maxWaitMillis);
            } catch (GetConnectionTimeoutException ex) {
                if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                    notFullTimeoutRetryCnt++;
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                    }
                    continue;
                }
                throw ex;
            }

            if (testOnBorrow) {
                boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if (!validate) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }

                    Connection realConnection = poolableConnection.conn;
                    discardConnection(realConnection);
                    continue;
                }
            } else {
                Connection realConnection = poolableConnection.conn;
                if (poolableConnection.conn.isClosed()) {
                    discardConnection(null); // 傳入null,避免重複關閉
                    continue;
                }

                if (testWhileIdle) {
                    final DruidConnectionHolder holder = poolableConnection.holder;
                    long currentTimeMillis             = System.currentTimeMillis();
                    long lastActiveTimeMillis          = holder.lastActiveTimeMillis;
                    long lastKeepTimeMillis            = holder.lastKeepTimeMillis;

                    if (lastKeepTimeMillis > lastActiveTimeMillis) {
                        lastActiveTimeMillis = lastKeepTimeMillis;
                    }

                    long idleMillis                    = currentTimeMillis - lastActiveTimeMillis;

                    long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;

                    if (timeBetweenEvictionRunsMillis <= 0) {
                        timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                    }

                    if (idleMillis >= timeBetweenEvictionRunsMillis
                            || idleMillis < 0 // unexcepted branch
                            ) {
                        boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                        if (!validate) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("skip not validate connection.");
                            }

                            discardConnection(realConnection);
                             continue;
                        }
                    }
                }
            }

            if (removeAbandoned) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.connectStackTrace = stackTrace;
                poolableConnection.setConnectedTimeNano();
                poolableConnection.traceEnable = true;

                activeConnectionLock.lock();
                try {
                    activeConnections.put(poolableConnection, PRESENT);
                } finally {
                    activeConnectionLock.unlock();
                }
            }

            if (!this.defaultAutoCommit) {
                poolableConnection.setAutoCommit(false);
            }

            return poolableConnection;
        }
    }

參考文章:http://zhengjianglong.cn/2019/07/14/framework/druid-db-connection/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章