alibaba druid 连接池获取数据库连接过程解析

alibaba druid 连接池获取数据库连接过程解析

1.初始化过程

主要看DruidDataSource这个类
看怎么创建连接的要从getConnection()这个方法看起
getConnection()中有个init()初始化方法
init做的事情就是加锁,然后判断各种条件参数是否符合要求,给内部各种参数赋值
需要关心的就是下面的代码

connections = new DruidConnectionHolder[maxActive];
evictConnections = new DruidConnectionHolder[maxActive];
keepAliveConnections = new DruidConnectionHolder[maxActive];         

这三个数组里面放了具体的连接
一个存放所有连接,一个回收的连接,一个放存活在保持的连接
需要注意的是这三个数组里面放的是DruidConnectionHolder对象,这个对应是对PhysicalConnectionInfo物理连接信息进行包装的一个类,而PhysicalConnectionInfo又是对Connection的一次包装

下面这段代码就是具体的初始化连接池的过程
主要分为同步和异步

异步就是将创建连接的任务交给一个线程池去处理,一直异步创建知道达到initialSize为止
同步的过程大致差不多

if (createScheduler != null && asyncInit) {
                for (int i = 0; i < initialSize; ++i) {
                    createTaskCount++;
                    CreateConnectionTask task = new CreateConnectionTask(true);
                    this.createSchedulerFuture = createScheduler.submit(task);
                }
            } else if (!asyncInit) {
                // init connections
                while (poolingCount < initialSize) {
                    try {
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                        connections[poolingCount++] = holder;
                    } catch (SQLException ex) {
                        LOG.error("init datasource error, url: " + this.getUrl(), ex);
                        if (initExceptionThrow) {
                            connectError = ex;
                            break;
                        } else {
                            Thread.sleep(3000);
                        }
                    }
                }

                if (poolingCount > 0) {
                    poolingPeak = poolingCount;
                    poolingPeakTime = System.currentTimeMillis();
                }
            }

里面有个内部类CreateConnectionTask,这个类是一个实现了runnable接口的任务类,用于创建连接
下面看具体方法runInternal

PhysicalConnectionInfo physicalConnection = null;
try {
	physicalConnection = createPhysicalConnection();
} catch (OutOfMemoryError e) {

}

createPhysicalConnection方法之前有一些加锁判断条件之类的代码,我们这里忽略,只关心创建的方法
createPhysicalConnection这个方法在抽象父类DruidAbstractDataSource中
做的事情就是根据各种配置中读取的参数来创建一个物理连接
创建好了之后通过调用put方法,将physicalConnection封装成DruidConnectionHolder然后放入到connections数组中

boolean result = put(physicalConnection);
protected boolean put(PhysicalConnectionInfo physicalConnectionInfo) {
        DruidConnectionHolder holder = null;
        try {
            holder = new DruidConnectionHolder(DruidDataSource.this, physicalConnectionInfo);
        } catch (SQLException ex) {
            lock.lock();
            try {
                if (createScheduler != null) {
                    createTaskCount--;
                }
            } finally {
                lock.unlock();
            }
            LOG.error("create connection holder error", ex);
            return false;
        }

        return put(holder);
    }
private boolean put(DruidConnectionHolder holder) {
        lock.lock();
        try {
            if (poolingCount >= maxActive) {
                return false;
            }
            connections[poolingCount] = holder;
            incrementPoolingCount();

            if (poolingCount > poolingPeak) {
                poolingPeak = poolingCount;
                poolingPeakTime = System.currentTimeMillis();
            }

            notEmpty.signal();
            notEmptySignalCount++;

            if (createScheduler != null) {
                createTaskCount--;

                if (poolingCount + createTaskCount < notEmptyWaitThreadCount //
                    && activeCount + poolingCount + createTaskCount < maxActive) {
                    emptySignal();
                }
            }
        } finally {
            lock.unlock();
        }
        return true;
    }

2.那么初始化好了之后该如何获取连接呢?

我们还是从getConnection()方法看起
getConnection()调用了getConnection(long maxWaitMillis) 其中已经init()过了,所以这次inti()方法直接return了
我们主要看getConnectionDirect(maxWaitMillis)这个方法

    @Override
    public DruidPooledConnection getConnection() throws SQLException {
        return getConnection(maxWait);
    }

    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)这个方法很长,主要是一些状态更新很判断过程,我们重点看下面的部分

public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
    //...
    DruidPooledConnection poolableConnection;
    try {
        poolableConnection = getConnectionInternal(maxWaitMillis);
    } catch (GetConnectionTimeoutException ex) {
    //...
    }
    //...

getConnectionInternal这个方法会从数组中获取holder对象,然后封装成connection返回
重点看如下部分pollLast(nanos)和takeLast()都会从connections数组尾端中拿到holder对象
然后封装成DruidPooledConnection对象返回

private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
    DruidConnectionHolder holder;
    //...
    if (maxWait > 0) {
        holder = pollLast(nanos);
    } else {
        holder = takeLast();
    }
    //...
    DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
    return poolalbeConnection;
}

大致过程如此,实际情况复杂很多,涉及到很多加锁通知,内部状态更新很多其他操作,有兴趣的朋友可以看看源码
本文中的源码是基于1.1.16版本

你可能感兴趣的:(alibaba druid 连接池获取数据库连接过程解析)