在《druid解析-DruidDataSource初始化》文章中提到过连接是通过getConnectionDirect()方法获取的,下面来看下该方法(代码有删减):
//入参表示获取连接的超时时间
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
int notFullTimeoutRetryCnt = 0;
//连接拿不到,不断重试
for (; ; ) {
DruidPooledConnection poolableConnection;
try {
poolableConnection = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
notFullTimeoutRetryCnt++;//超过一定失败次数,则获取连接失败并抛出异常
}
continue;//连接获取失败,重试
}
throw ex;
}
//testOnBorrow为true,则在返回连接前对连接校验
if (testOnBorrow) {
//检查连接是否符合要求,检查内容有:
//1)ValidConnectionChecker检查连接,2)执行validationQuery的SQL语句
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
discardConnection(poolableConnection.holder);//关闭连接
continue;
}
} else {
if (poolableConnection.conn.isClosed()) {
discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
continue;
}
//testWhileIdle为true的话,当连接空闲超过timeBetweenEvictionRunsMillis毫秒时,调用testConnectionInternal()对连接检验
if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
long currentTimeMillis = System.currentTimeMillis();
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
long lastExecTimeMillis = holder.lastExecTimeMillis;
long lastKeepTimeMillis = holder.lastKeepTimeMillis;
if (checkExecuteTime
&& lastExecTimeMillis != lastActiveTimeMillis) {
lastActiveTimeMillis = lastExecTimeMillis;
}
if (lastKeepTimeMillis > lastActiveTimeMillis) {
lastActiveTimeMillis = lastKeepTimeMillis;
}
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
if (timeBetweenEvictionRunsMillis <= 0) {
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
//idleMillis表示连接的从上一次使用结束后到现在为止的空闲时间
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
//校验连接
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
discardConnection(poolableConnection.holder);
continue;
}
}
}
}
if (removeAbandoned) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.connectStackTrace = stackTrace;
poolableConnection.setConnectedTimeNano();
poolableConnection.traceEnable = true;
activeConnectionLock.lock();
try {
//将连接保存在Map中,druid有一个清理线程,清理线程将该Map里面保存的不可用的连接销毁
activeConnections.put(poolableConnection, PRESENT);
} finally {
activeConnectionLock.unlock();
}
}
if (!this.defaultAutoCommit) {
poolableConnection.setAutoCommit(false);//设置连接非自动提交,默认连接是自动提交
}
return poolableConnection;
}
}
getConnectionDirect()相对来说比较简单:
- 调用getConnectionInternal()获取连接;
- 如果testOnBorrow=true,则对连接进行校验;
- 如果testWhileIdle=true,testOnBorrow=false且连接空闲超过一定时间,则对连接进行校验;
- 如果removeAbandoned=true,则将连接放到一个Map中,供清理线程对连接清理。
下面介绍getConnectionInternal()方法,该方法是获取连接的核心方法(方法有删减):
private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
final int maxWaitThreadCount = this.maxWaitThreadCount;
DruidConnectionHolder holder;
for (boolean createDirect = false; ; ) {
if (createDirect) {//如果createDirect=true,则直接创建连接,不通过连接池获取
//createStartNanosUpdater没发现有什么用途
createStartNanosUpdater.set(this, System.nanoTime());
if (creatingCountUpdater.compareAndSet(this, 0, 1)) {
//创建连接
PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
holder = new DruidConnectionHolder(this, pyConnInfo);
holder.lastActiveTimeMillis = System.currentTimeMillis();
creatingCountUpdater.decrementAndGet(this);
directCreateCountUpdater.incrementAndGet(this);
boolean discard;
lock.lock();
try {
if (activeCount < maxActive) {
activeCount++;//活动连接数加1
holder.active = true;
if (activeCount > activePeak) {
activePeak = activeCount;
activePeakTime = System.currentTimeMillis();
}
break;//获取到连接,直接退出
} else {
//当活动连接数超过最大活动连接数时,需要将当前连接销毁
discard = true;
}
} finally {
lock.unlock();
}
if (discard) {
JdbcUtils.close(pyConnInfo.getPhysicalConnection());
}
}
}
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("interrupt", e);
}
try {
//如果当前等待获取连接的应用线程数超过了maxWaitThreadCount,则抛出异常
//这也就意味着禁止后续的线程获取连接了
if (maxWaitThreadCount > 0
&& notEmptyWaitThreadCount >= maxWaitThreadCount) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
+ lock.getQueueLength());
}
if (onFatalError
&& onFatalErrorMaxActive > 0
&& activeCount >= onFatalErrorMaxActive) {
connectErrorCountUpdater.incrementAndGet(this);
throw new SQLException(lastFatalError);
}
//计数器,表示总共获取连接的次数
connectCount++;
//如果定时任务不为空,连接池个数为0,当前活动连接数小于最大连接数,
//定时任务里面有待执行任务,且没有其他线程正在创建连接,
//那么允许通过Driver直接创建连接
if (createScheduler != null
&& poolingCount == 0
&& activeCount < maxActive
&& creatingCountUpdater.get(this) == 0
&& createScheduler instanceof ScheduledThreadPoolExecutor) {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
if (executor.getQueue().size() > 0) {
createDirect = true;
continue;
}
}
//下面的方法为从连接池里面拿连接
if (maxWait > 0) {
holder = pollLast(nanos);
} else {
holder = takeLast();
}
if (holder != null) {
if (holder.discard) {
continue;
}
//活动连接数加1
activeCount++;
holder.active = true;
if (activeCount > activePeak) {
activePeak = activeCount;
activePeakTime = System.currentTimeMillis();
}
}
} catch (InterruptedException e) {
throw new SQLException(e.getMessage(), e);
} catch (SQLException e) {
throw e;
} finally {
lock.unlock();
}
break;
}
if (holder == null) {
//如果连接没有获取到,则抛出异常,这里代码做了省略
}
//用于统计,如果连接使用一次,计数器加1
holder.incrementUseCount();
DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
return poolalbeConnection;
}
getConnectionInternal()方法有两种方式返回连接,一种是从连接池里面获取,另一种是直接调用Driver的创建连接方法获取,但是要使用第二种需要满足非常苛刻的条件:
- 定时任务对象不为null;
- 连接池个数为0;
- 当前活动连接数小于最大活动连接数;
- 定时任务里面有待执行任务;
- 没有其他线程正在创建连接。
方法getConnectionInternal()通过pollLast(nanos)或者takeLast()从连接池里面拿连接,而且都是从连接池的最后获取,这就意味着越频繁使用的连接越会在下次使用。这两个方法的最后都是执行下面的逻辑:
//连接池个数减1
decrementPoolingCount();
//从连接池的最后获取连接,last对象就是方法返回值
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null;
pollLast(nanos)和takeLast()两个方法的区别在于,pollLast()会不断的尝试获取连接,当获取连接的等待时间超过了入参nanos,该方法直接返回null,而takeLast()会一直尝试获取连接,直到获取到连接为止。