druid 源码的两个主要逻辑,第一个是获取连接,第二个是连接使用完之后释放。我们第一天以这两个地方作为入口看下主流程。
获取连接:
测试方法:
public void test_conn() throws Exception {
PooledConnection conn = dataSource.getPooledConnection();
conn.close();
}
复制代码
调用堆栈:
getPooledConnection:1415, DruidDataSource (com.alibaba.druid.pool)
getConnection:1409, DruidDataSource (com.alibaba.druid.pool)
getConnectionDirect:1429, DruidDataSource (com.alibaba.druid.pool)
getConnectionInternal:1692, DruidDataSource (com.alibaba.druid.pool)
takeLast:2208, DruidDataSource (com.alibaba.druid.pool)
复制代码
分步跟进:
● getPooledConnection、getConnection 为简单的调用,没有逻辑
● getConnection 中包含两步,driver 初始化 init 和 getConnectionDirect 获取连接
一、com.alibaba.druid.pool.DruidDataSource#init 初始化数据库 driver
1、首先通过 lock 控制并发,保证初始化的时候线程安全,这块控制并发的时候,处理方式类似双重锁校验
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new SQLException("interrupt", e);
}
复制代码
2、创建对应的连接池数组,数组长度 maxActive
connections = new DruidConnectionHolder[maxActive];
evictConnections = new DruidConnectionHolder[maxActive];
keepAliveConnections = new DruidConnectionHolder[maxActive];
复制代码
3、为连接池数组创建连接
if (createScheduler != null && asyncInit) {
for (int i = 0; i < initialSize; ++i) {
submitCreateTask(true);
}
} 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();
}
}
复制代码
二、com.alibaba.druid.pool.DruidDataSource#getConnectionDirect 获取连接
1、com.alibaba.druid.pool.DruidDataSource#getConnectionInternal
也是先走同步逻辑
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("interrupt", e);
}
复制代码
2、com.alibaba.druid.pool.DruidDataSource#takeLast
一个很精巧的逻辑,异步创建线程,获取线程的地方通过 lock.wait()的方式等待有连接出现
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();
}
}
复制代码
先把空闲连接数减 1,然后获取对应线程
decrementPoolingCount();
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null;
复制代码
ps:我比较好奇这里为什么不用 java 原生的阻塞队列实现,而是使用数组的方式,是为了提升性能么?
释放连接:
测试方法:
public void test_conn() throws Exception {
PooledConnection conn = dataSource.getPooledConnection();
conn.close();
}
复制代码
调用堆栈:
recycle:2032, DruidDataSource (com.alibaba.druid.pool)
recycle:351, DruidPooledConnection (com.alibaba.druid.pool)
syncClose:326, DruidPooledConnection (com.alibaba.druid.pool)
close:270, DruidPooledConnection (com.alibaba.druid.pool)
复制代码
时间关系先写到这里,释放连接的分步执行过程下次补上
评论