【Druid源码阅读】2. 初始化连接之异步创建连接

11 篇文章 0 订阅

目录

1. 异步参数

2. 创建连接任务

3. 创建连接


今天就连着昨天的思路,来看下连接到底是如何初始化的。

第14步: 初始化连接,数量为设置的初始连接数,并存放到数组中;(真正创建连接在这一步中)

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

1. 异步参数

异步初始化需要设置2个参数:

ScheduledExecutorService createScheduler 和 boolean asyncInit = false;

public void setCreateScheduler(ScheduledExecutorService createScheduler) {
    // 已经初始化之后再设置抛出异常
    if (isInited()) {
        throw new DruidRuntimeException("dataSource inited.");
    }
    this.createScheduler = createScheduler;
}

 在 DruidDataSource 构造方法中,会从系统的环境变量中取值设置;

或者手动调用 Setter 设置。


public DruidDataSource(boolean fairLock){
    super(fairLock);

    configFromPropety(System.getProperties());
}


configFromPropety(Properties)
{
    Boolean value = getBoolean(properties, "druid.asyncInit"); // compatible for early versions
    if (value != null) {
        this.setAsyncInit(value);
    }
}

public void setAsyncInit(boolean asyncInit) {
    this.asyncInit = asyncInit;
}

2. 创建连接任务

for循环会创建 初始化连接数 的创建任务:

submitCreateTask(true);

private void submitCreateTask(boolean initTask) {
    createTaskCount++;
    // 实例化创建连接任务对象
    CreateConnectionTask task = new CreateConnectionTask(initTask);
    if (createTasks == null) {
        createTasks = new long[8];
    }

    // 为 createTasks 数组扩容,数组保存的是创建的任务对象 taskId
    boolean putted = false;
    for (int i = 0; i < createTasks.length; ++i) {
        if (createTasks[i] == 0) {
            createTasks[i] = task.taskId;
            putted = true;
            break;
        }
    }
    if (!putted) {
        long[] array = new long[createTasks.length * 3 / 2];
        System.arraycopy(createTasks, 0, array, 0, createTasks.length);
        array[createTasks.length] = task.taskId;
        createTasks = array;
    }

    // 提交刚才创建的任务对象到执行器里
    this.createSchedulerFuture = createScheduler.submit(task);
}

看一下这个任务对象 CreateConnectionTask:

2个构造方法,都会使用原子字段更新器累加,并记录为 taskId:

public CreateConnectionTask() {
    taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);
}

public CreateConnectionTask(boolean initTask) {
    taskId = createTaskIdSeedUpdater.getAndIncrement(DruidDataSource.this);
    this.initTask = initTask;
}

因为实现了 Runnable 并且被提交到执行器了,关键方法是 run() 方法:

@Override
public void run() {
    runInternal();
}

里面是一个 for 死循环,不创建连接就一直循环。

因为是异步的,并且判断退出时,使用了DruidDataSource 的字段值进行判断,存在多线程并发安全问题,所以先加锁;当不满足条件时,不退出方法,继续执行时解锁;

下面是创建连接的方法:

PhysicalConnectionInfo physicalConnection = null;

try {
    physicalConnection = createPhysicalConnection();
} catch(Error e) {... // 异常处理}

if (physicalConnection == null) {
    continue;
}
createPhysicalConnection()

...

Connection conn = null;

...

try {
    conn = createPhysicalConnection(url, physicalConnectProperties);
    connectedNanos = System.nanoTime();

    if (conn == null) {
        throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);
    }

    initPhysicalConnection(conn, variables, globalVariables);
    initedNanos = System.nanoTime();

    validateConnection(conn);
    validatedNanos = System.nanoTime();

    setFailContinuous(false);
    setCreateError(null);
}

3. 创建连接

public Connection createPhysicalConnection(String url, Properties info) throws SQLException {
    Connection conn;
    if (getProxyFilters().isEmpty()) {
        // 没有代理过滤器时,直接使用驱动创建连接
        conn = getDriver().connect(url, info);
    } else {
        // 存在代理过滤器时,使用代理过滤器责任链创建连接
        conn = new FilterChainImpl(this).connection_connect(info);
    }

    createCountUpdater.incrementAndGet(this);

    return conn;
}

public ConnectionProxy connection_connect(Properties info) throws SQLException {
    if (this.pos < filterSize) {
        return nextFilter()
                .connection_connect(this, info);
    }

    Driver driver = dataSource.getRawDriver();
    String url = dataSource.getRawJdbcUrl();

    // 最终还是使用驱动创建连接
    Connection nativeConnection = driver.connect(url, info);

    if (nativeConnection == null) {
        return null;
    }

    return new ConnectionProxyImpl(dataSource, nativeConnection, info, dataSource.createConnectionId());
}

创建连接后会封装到  PhysicalConnectionInfo 对象中,然后使用 put 方法放到连接池中:

boolean result = put(physicalConnection);
if (!result) {
    JdbcUtils.close(physicalConnection.getPhysicalConnection());
    LOG.info("put physical connection to pool failed.");
}
protected boolean put(PhysicalConnectionInfo physicalConnectionInfo) {
    DruidConnectionHolder holder = null;
    try {
        // 将 physicalConnectionInfo 封装到 DruidConnectionHolder 中,还记得上一篇中重要的那3个数组吗?
        holder = new DruidConnectionHolder(DruidDataSource.this, physicalConnectionInfo);
    } catch (SQLException ex) {
        ... // 异常处理
    }

    return put(holder, physicalConnectionInfo.createTaskId, false);
}
private boolean put(DruidConnectionHolder holder, long createTaskId, boolean checkExists) {
    lock.lock();
    try {
        if (this.closing || this.closed) {
            return false;
        }

        // 大于最大活跃数时,不再保存到数组
        if (poolingCount >= maxActive) {
            if (createScheduler != null) {
                clearCreateTask(createTaskId);
            }
            return false;
        }

        if (checkExists) {
            for (int i = 0; i < poolingCount; i++) {
                if (connections[i] == holder) {
                    return false;
                }
            }
        }

        // 保存到数组中
        connections[poolingCount] = holder;
        incrementPoolingCount();

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

        // 唤醒获取连接时,因没有可用连接而等待的线程
        notEmpty.signal();
        notEmptySignalCount++;

        if (createScheduler != null) {
            clearCreateTask(createTaskId);

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

到此,粗略的把异步创建线程并保存到数组中分析完了。

其中,有很多条件判断,还是一知半解,还需要进一步分析。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值