【Druid源码阅读】6. 初始化后获取连接

本文详细分析了Druid连接池获取连接的实现,包括当存在过滤器和无过滤器的情况。在无过滤器时,直接调用getConnectionDirect方法,该方法涉及了连接池的等待、超时重试以及takeLast方法,用于从连接池尾部获取连接。同时,emptySignal方法用于向创建连接线程发送信号,触发创建新的连接。整个过程展示了Druid在高并发下的连接管理和线程同步策略。

前几天把 init 初始化方法大概分析了下,今天继续,把获取连接的部分分析完。

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

这里有个判断,分为有过滤器和无过滤器,运行单测,没有设定过滤器,默认会走 getConnectionDirect:(这里入参 maxWaitMillis 默认值为 -1)

public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
    int notFullTimeoutRetryCnt = 0;
    for (;;) {
        // handle notFullTimeoutRetry
        DruidPooledConnection poolableConnection;
        try {
            // 获取连接
            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;
        }
...

getConnectionInternal 方法前面一些异常场景的判断,最后会走到如下代码处:

if (maxWait > 0) {
    holder = pollLast(nanos);
} else {
    holder = takeLast();
}

入参为-1,进入 else 分支。

takeLast 通过名字可以得知,应该是从数组最后面获取。

DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
    try {
        while (poolingCount == 0) {
            emptySignal(); // send signal to CreateThread create connection

            if (failFast && isFailContinuous()) {
                throw new DataSourceNotAvailableException(createError);
            }

            notEmptyWaitThreadCount++;
            if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
                notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
            }
            try {
                notEmpty.await(); // signal by recycle or creator
            } finally {
                notEmptyWaitThreadCount--;
            }
            notEmptyWaitCount++;

            if (!enable) {
                connectErrorCountUpdater.incrementAndGet(this);
                if (disableException != null) {
                    throw disableException;
                }

                throw new DataSourceDisableException();
            }
        }
    } catch (InterruptedException ie) {
        notEmpty.signal(); // propagate to non-interrupted thread
        notEmptySignalCount++;
        throw ie;
    }

进到代码里,显示一个 while 循环,第一个方法是 emptySignal(); 并且有注释: // send signal to CreateThread create connection,向创建线程发送信号去创建连接。

private void emptySignal() {
    if (createScheduler == null) {
        // 通知创建连接线程继续执行
        empty.signal();
        return;
    }

    // 如果创建任务数量大于等于最大创建任务数量,返回
    if (createTaskCount >= maxCreateTaskCount) {
        return;
    }

    // 如果活动数量+线程池内数量+创建线程数量大于等于最大活跃数,返回
    if (activeCount + poolingCount + createTaskCount >= maxActive) {
        return;
    }

    // 如果前面的分支都没进,代表需要创建任务,并且与初始化时创建任务区分
    submitCreateTask(false);
}

默认 createScheduler 是没有设置值的,为null。

看到 empty.sinal() 方法,想到 CreateConnectionThread 创建连接线程的 run() 方法中,2处 empty.await(); 就是要通知在 empty 条件上等待的创建连接线程继续向下执行。

if (emptyWait) {
    // 必须存在线程等待,才创建连接
    if (poolingCount >= notEmptyWaitThreadCount //
            && (!(keepAlive && activeCount + poolingCount < minIdle))
            && !isFailContinuous()
    ) {
        empty.await();
    }

    // 防止创建超过maxActive数量的连接
    if (activeCount + poolingCount >= maxActive) {
        empty.await();
        continue;
    }
}

最后,返回数组的最后一个 DruidConnectionHolder,并把连接池数量减1,连接池数组置空:

decrementPoolingCount();
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null;

return last;

返回后,封装到 DruidPooledConnection 中返回:

DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
return poolalbeConnection;

后续还做了一些校验,最终返回给调用方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值