1、CreateConnectionThread 线程
1.1 线程创建
Druid连接池运行后,该线程大部分情况下都处于WAITING状态,通过 jstack -l PID 查看。
protected void createAndStartCreatorThread() {
if (createScheduler == null) {
String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this);
//创建线程并启动
createConnectionThread = new CreateConnectionThread(threadName);
createConnectionThread.start();
return;
}
initedLatch.countDown();
}
1.2 执行过程
public void run() {
//利用循环,获取连接
for (;;) {
try {
//获取锁,可响应中断
lock.lockInterruptibly();
} catch (InterruptedException e2) {
break;
}
try {
boolean emptyWait = true;
...
//判断是否能够创建连接,否则通过Condition条件锁将线程挂起
if (emptyWait) {
// 必须存在线程等待,才创建连接
if (poolingCount >= notEmptyWaitThreadCount //等待的连接数大于线程池连接数,才创建
&& (!(keepAlive && activeCount + poolingCount < minIdle))
&& !isFailContinuous()) {
empty.await();
}
// 活动线程数+可用线程数小于maxActive,才创建
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}
}
} catch (InterruptedException e) {
...
break;
} finally {
lock.unlock();
}
PhysicalConnectionInfo connection = null;
try {
//创建物理连接
connection = createPhysicalConnection();
} catch (SQLException e) {
//重试和故障转移
...
} catch (RuntimeException e) {
LOG.error("create connection RuntimeException", e);
setFailContinuous(true);
continue;
} catch (Error e) {
LOG.error("create connection Error", e);
setFailContinuous(true);
break;
}
if (connection == null) {
continue;
}
//将物理连接PUT到连接池,通知消费线程来获取
boolean result = put(connection);
if (!result) {
//PUT失败将连接关闭
JdbcUtils.close(connection.getPhysicalConnection());
LOG.info("put physical connection to pool failed.");
}
errorCount = 0; // reset errorCount
if (closing || closed) {
break;
}
}
}
}
DruidAbstractDataSource 创建时创建了两个条件锁。
public DruidAbstractDataSource(boolean lockFair){
lock = new ReentrantLock(lockFair);
//当连接数不够时将线程阻塞
notEmpty = lock.newCondition();
//需要创建新连接时被唤醒线程,否则将线程阻塞
empty = lock.newCondition();
}
初始化完成后 CreateConnectionThread线程在empty条件锁阻塞住,当连接池中的连接不够时会唤醒该线程,同时通过notEmpty.await() 阻塞用户线程,通过连接的回收或创建新的连接被唤醒这也是一个生产者和消费者模式,可以将CreateConnectionThread 作为生产者的线程。
2、DestroyConnectionThread线程
线程池中的销毁线程,将connections 连接池中的连接经过各类条件判断进行分类,最后分别对keepAliveConnections和evictConnections遍历处理,检测可用继续放回connections中,检测不可用丢弃。
protected void createAndStartDestroyThread() {
destroyTask = new DestroyTask();
//可配置定时任务的线程池或创建线程的方式
if (destroyScheduler != null) {
long period = timeBetweenEvictionRunsMillis;
if (period <= 0) {
period = 1000;
}
destroySchedulerFuture = destroyScheduler.scheduleAtFixedRate(destroyTask, period, period,
TimeUnit.MILLISECONDS);
initedLatch.countDown();
return;
}
String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this);
destroyConnectionThread = new DestroyConnectionThread(threadName);
destroyConnectionThread.start();
}
2.1 执行过程
DestroyConnectionThread 线程通过配置timeBetweenEvictionRunsMillis 间隔执行默认60秒一次,通过DestroyTask.run方法执行。
public class DestroyTask implements Runnable {
@Override
public void run() {
//连接池检测
shrink(true, keepAlive);
//配置removeAbandoned=true
if (isRemoveAbandoned()) {
//判断连接池内的连接最新GET时间,超时removeAbandonedTimeoutMillis则close连接池
//每次getConnection更新connectedTimeNano时间
removeAbandoned();
}
}
}
2.2 shrink过程
public void shrink(boolean checkTime, boolean keepAlive) {
try {
//获取锁
lock.lockInterruptibly();
} catch (InterruptedException e) {
return;
}
...
try {
if (!inited) {
return;
}
//大于minIdle的连接数=池中连接数-最小空闲连接数
final int checkCount = poolingCount - minIdle;
//遍历数组中的连接对象
for (int i = 0; i < poolingCount; ++i) {
//对线程池的连接对象进行循环判断
DruidConnectionHolder connection = connections[i];
//如果发生了报错则连接池则放入活性检测连接池
if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) {
keepAliveConnections[keepAliveCount++] = connection;
continue;
}
if (checkTime) {
//如果配置连接过期时间(默认-1)
if (phyTimeoutMillis > 0) {
long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
//如果超过了连接时间则需要进行抛弃
if (phyConnectTimeMillis > phyTimeoutMillis) {
evictConnections[evictCount++] = connection;
continue;
}
}
long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;
if (idleMillis < minEvictableIdleTimeMillis
&& idleMillis < keepAliveBetweenTimeMillis
) {
break;
}
//连接空闲时间大于 minEvictableIdleTimeMillis 则判断是否关闭连接
if (idleMillis >= minEvictableIdleTimeMillis) {
//连接数大于 minIdle 加入关闭连接数组
if (checkTime && i < checkCount) {
evictConnections[evictCount++] = connection;
continue;
//连接数不大于 minIdle 大于最大空闲时间,加入关闭连接数组
} else if (idleMillis > maxEvictableIdleTimeMillis) {
evictConnections[evictCount++] = connection;
continue;
}
}
//如果配置了keepAlive并接空闲时间超过keepAliveBetweenTimeMillis则放入活性检测连接池
if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
keepAliveConnections[keepAliveCount++] = connection;
}
} else {
if (i < checkCount) {
evictConnections[evictCount++] = connection;
} else {
break;
}
}
}
//需要从可用连接池删除个数,将先添加到连接池的进行删除
if (removeCount > 0) {
//从线程池复制开始位置=removeCount,复制到connections从start=0,length=(poolingCount-removeCount)
System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
//connections从start(poolingCount - removeCount)到poolingCount设置为空
Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
//连接池可用数量减删除数量
poolingCount -= removeCount;
}
...
} finally {
lock.unlock();
}
//将抛弃连接池的连接进行关闭
if (evictCount > 0) {
for (int i = 0; i < evictCount; ++i) {
DruidConnectionHolder item = evictConnections[i];
Connection connection = item.getConnection();
JdbcUtils.close(connection);
destroyCountUpdater.incrementAndGet(this);
}
Arrays.fill(evictConnections, null);
}
//需要检测可用性的连接>0
if (keepAliveCount > 0) {
for (int i = keepAliveCount - 1; i >= 0; --i) {
DruidConnectionHolder holer = keepAliveConnections[i];
Connection connection = holer.getConnection();
holer.incrementKeepAliveCheckCount();
boolean validate = false;
try {
//检测连接的可用性
this.validateConnection(connection);
validate = true;
} catch (Throwable error) {
if (LOG.isDebugEnabled()) {
LOG.debug("keepAliveErr", error);
}
}
boolean discard = !validate;
if (validate) {
holer.lastKeepTimeMillis = System.currentTimeMillis();
//如果连接可用则PUT到连接池
boolean putOk = put(holer, 0L, true);
if (!putOk) {
discard = true;
}
}
if (discard) {
try {
//连接不可用则进行关闭
connection.close();
} catch (Exception e) {
}
lock.lock();
try {
discardCount++;
if (activeCount + poolingCount <= minIdle) {
//将创建连接的线程唤醒
emptySignal();
}
} finally {
lock.unlock();
}
}
}
this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
Arrays.fill(keepAliveConnections, null);
}
if (needFill) {
lock.lock();
try {
//需要创建的连接数=最小连接数-(活动线程数+连接池可用线程数+创建连接线程数)
int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
for (int i = 0; i < fillCount; ++i) {
//将创建连接的线程唤醒
emptySignal();
}
} finally {
lock.unlock();
}
} else if (onFatalError || fatalErrorIncrement > 0) {
lock.lock();
try {
emptySignal();
} finally {
lock.unlock();
}
}
}
使用validateQuery查询探活连接消耗更大,JDBC提供了更轻量级的探活机制 Connection#isValid, Druid中MySQL通过MySqlValidConnectionChecker检测连接可用性,可使用ping的方式,jvm参数:-Ddruid.mysql.usePingMethod=true。