写点什么

druid 源码阅读 1——获取连接与释放连接

作者:张大彪
  • 2022 年 5 月 10 日
  • 本文字数:2020 字

    阅读完需:约 7 分钟

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)
复制代码


时间关系先写到这里,释放连接的分步执行过程下次补上

用户头像

张大彪

关注

还未添加个人签名 2018.04.25 加入

还未添加个人简介

评论

发布
暂无评论
druid源码阅读1——获取连接与释放连接_张大彪_InfoQ写作社区