ドルイドソースコード読み取り5-DruidDataSource縮小プロセス

この記事は「新人クリエーションセレモニー」活動に参加し、一緒にナゲットクリエーションの道を歩み始めました

縮小メソッドは、DestroyTaskスレッドで接続をリサイクルするための特定の実行メソッドです。

最初にロックを取得します。

try {
    lock.lockInterruptibly();
} catch (InterruptedException e) {
    return;
}
复制代码

その後、初期化状態が完了したかどうかを判断する必要があります。非同期初期化を使用している場合は、DestoryTaskスレッドが開始されている可能性がありますが、接続プールは初期化されていません。

if (!inited) {
    return;
}
复制代码

その後、接続プール内の接続がトラバースされます。接続では、接続可能な接続の数がpoolingCount変数に記録されます。このとき、checkCountを記録する必要があります。この変数はcheckCount = poolingCount --minIdleです。つまり、checkCountは、接続プール内の接続数から、minIdleを設定するためのアイドル接続の最小数を引いたものです。

次に、checkTimeロジックを入力します。checkTimeはshrinkを呼び出すときに渡されるパラメーターです。通常、このパラメーターはDestroyTaskを呼び出すときにtrueになります。その後、チェックのパラメータは次のとおりです。

  • 物理接続がタイムアウトしたかどうかを確認します:phyConnectTimeMillis>phyTimeoutMillis。タイムアウトした場合は、evictConnections配列への現在の接続をマークし、現在のループを終了します。
  • アイドル時間が経過したかどうかを確認します。

アイドル時間が構成されたminEvictableIdleTimeMillis時間よりも短く、構成されたkeepAliveBetweenTimeMillis(idleMillis <minEvictableIdleTimeMillis && idleMillis <keepAliveBetweenTimeMillis)よりも短い場合、ループは終了します。逆に、idleMillisがminEvictableIdleTimeMillisより大きいか、maxEvictableIdleTimeMillisより大きい場合、evictConnections配列でマークされます。

  • 判断keepAlive是否超时:如果idleMillis >= keepAliveBetweenTimeMillis,则标记到keepAliveConnections数组。

如果checkTime为false,则将小于checkCount的全部连接都标记到evictConnections数组。

if (i < checkCount) {
    evictConnections[evictCount++] = connection;
} else {
    break;
}
复制代码

这之后进行removeCount的处理,removeCount = evictCount + keepAliveCount; 处理逻辑如下:

if (removeCount > 0) {
    //将connections从removeCount到poolingCount的连接向前移动poolingCount - removeCount。
    System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
    //将poolingCount - removeCount后续部分都置为空。
    Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
    poolingCount -= removeCount;
}
复制代码

这个逻辑实质上是将connections中计算出来的前N项都移除。 之前一直不理解这个逻辑,实际上需要详细看一下for循环中的逻辑。for循环中,如果checkTime为false,则直接将前面checkCount个连接都移除。 反之,由于connections中,通过recycle方法,将放回的连接都放在connections数组的最后面。get的连接也是从connections的尾部获取,那么可以确保connections的连接,index小的连接最少被使用。 那么在这里确定了需要移除的连接数之后,直接就可以将connetions的前面checkCount个连接都移除。

移除之后,可以解锁。之后对移除的连接进行处理。

} finally {
    lock.unlock();
}
复制代码

对于evict的连接:

if (evictCount > 0) {
    for (int i = 0; i < evictCount; ++i) {
        DruidConnectionHolder item = evictConnections[i];
        Connection connection = item.getConnection();
        //关闭连接
        JdbcUtils.close(connection);
        //更新计数器
        destroyCountUpdater.incrementAndGet(this);
    }
    //将evictConnections清空
    Arrays.fill(evictConnections, null);
}
复制代码

关闭连接并清空evictConnections。

对于keepAliveCount连接,则需要分几种情况进行讨论:

if (keepAliveCount > 0) {
    // keep order
    for (int i = keepAliveCount - 1; i >= 0; --i) {
        DruidConnectionHolder holer = keepAliveConnections[i];
        Connection connection = holer.getConnection();
        holer.incrementKeepAliveCheckCount();

        boolean validate = false;
        //校验连接是否还可用
        try {
            this.validateConnection(connection);
            validate = true;
        } catch (Throwable error) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("keepAliveErr", error);
            }
            // skip
        }

        boolean discard = !validate;
        //如果可用,则直接put到connections中,放置到尾部。
        if (validate) {
            holer.lastKeepTimeMillis = System.currentTimeMillis();
            boolean putOk = put(holer, 0L, true);
            if (!putOk) {
                discard = true;
            }
        }
		
		//如果不可用,则关闭连接
        if (discard) {
            try {
                connection.close();
            } catch (Exception e) {
                // skip
            }

            lock.lock();
            //加锁更新计数器
            try {
                discardCount++;

                if (activeCount + poolingCount <= minIdle) {
                    emptySignal();
                }
            } finally {
                lock.unlock();
            }
        }
    }
    this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
    Arrays.fill(keepAliveConnections, null);
}
复制代码

对于keepalive状态的连接,为了更好的复用该连接,则首先判断该连接是否可用,如果可用,则调用put方法,将该连接的状态更新之后,放置到连接池的尾部。 可见,shrink中,并非所有的连接都会关闭,对于keepalive状态的连接,需要判断是否可用。可用的连接还可再次复用。

此时还有一种情况需要考虑,就是此时可用的连接仍然不够minIdle,那么连接池不满,需要继续创建连接。这个状态为needFill:

//keepAlive状态,且连接池中的连接加上被使用的连接仍然小于minIdle
if (keepAlive && poolingCount + activeCount < minIdle) {
    needFill = true;
}
复制代码

处理逻辑:

if (needFill) {
//加锁
    lock.lock();
    try {
        //如果minIdle 减去activeCount + poolingCount + createTaskCount 仍然不满,则通知创建线程创建连接
        int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
        for (int i = 0; i < fillCount; ++i) {
            emptySignal();
        }
        //解锁
    } finally {
        lock.unlock();
    }
} else if (onFatalError || fatalErrorIncrement > 0) {
    lock.lock();
    try {
        emptySignal();
    } finally {
        lock.unlock();
    }
}
复制代码

needFill和onFatalError 都需要通知生产者继续创建连接。

至此,shrink方法分析完毕。

おすすめ

転載: juejin.im/post/7078495292407939080