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)
复制代码
时间关系先写到这里,释放连接的分步执行过程下次补上
评论