目录
今天就连着昨天的思路,来看下连接到底是如何初始化的。
第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;
}
到此,粗略的把异步创建线程并保存到数组中分析完了。
其中,有很多条件判断,还是一知半解,还需要进一步分析。