1、testOnBorrow分析
参数定义:testOnBorrow:获取连接时检测,即每次使用连接时都会测试当前连接是否可用,开启后对性能有些影响,官方不建议开启false
if判断中如果配置了filters那么走责任链否则进行getConnectionDirect
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
// 在初始化dataSource时并不会执行init,而是在第一次获取连接的时候进行init
init();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
进入getConnectionDirect
//testOnBorrow:获取连接时检测,即每次使用连接时都会测试当前连接是否可用,开启后对性能有些影响,官方不建议开启false
if (testOnBorrow) {
boolean validate =
testConnectionInternal(poolableConnection.holder,poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(poolableConnection.holder);
continue;
}
}
2、testOnReturn分析
参数定义:testOnReturn:连接放回连接池时检测,同testOnBorrow影响性能,官方不建议开启false
回收连接时进行连接的可用性分析
/**
* 回收连接
*/
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
。。。
if (testOnReturn) {
boolean validate = testConnectionInternal(holder, physicalConnection);
if (!validate) {
JdbcUtils.close(physicalConnection);
destroyCountUpdater.incrementAndGet(this);
lock.lock();
try {
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
} finally {
lock.unlock();
}
return;
}
}
。。。
}
回收连接不是本次的重点,下次重点分析回收连接过程!
3、testWhileIdle分析 --- 重点!!!
参数定义:testWhileIdle:空闲时检测,testOnBorrow为false时,会进行testWhileIdle检查,testWhileIdle默认也是true,即不在每次都检测连接是否可用,而是空闲时间检测,官方建议开启true
进入getConnectionDirect
/*
testOnBorrow:获取连接时检测,即每次使用连接时都会测试当前连接是否可用,开启后对性能有些影响,官方不建议开启false
*/
if (testOnBorrow) {
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(poolableConnection.holder);
continue;
}
} else {
if (poolableConnection.conn.isClosed()) {
discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
continue;
}
// 如果没开启testOnBorrow,并且开启了testWhileIdle
// 空闲时检测,testOnBorrow为false时,会进行testWhileIdle检查,testWhileIdle默认也是true,即不在每次都检测连接是否可用,而是空闲时间检测,官方建议开启true
if (testWhileIdle) {
// 获取上面getConnectionInternal时拿到封装好的连接
final DruidConnectionHolder holder = poolableConnection.holder;
long currentTimeMillis = System.currentTimeMillis();
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
long lastExecTimeMillis = holder.lastExecTimeMillis;
long lastKeepTimeMillis = holder.lastKeepTimeMillis;
// 更新当前连接的最后活跃时间
// lastActiveTimeMillis:连接最后活跃时间
if (checkExecuteTime
&& lastExecTimeMillis != lastActiveTimeMillis) {
lastActiveTimeMillis = lastExecTimeMillis;
}
/*
lastKeepTimeMillis:连接的上次操作时间
lastKeepTimeMillis > lastActiveTimeMillis时,更新连接最后活跃时间为上次活跃时间
*/
if (lastKeepTimeMillis > lastActiveTimeMillis) {
lastActiveTimeMillis = lastKeepTimeMillis;
}
// 空闲时间
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
/*
timeBetweenEvictionRunsMillis:在指定周期内检测连接的有效性,默认1分钟
*/
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
// 开启了testWhileIdle 但是 timeBetweenEvictionRunsMillis <= 0时,将检测周期配置为1分钟
if (timeBetweenEvictionRunsMillis <= 0) {
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
// 开始检测逻辑
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
// 连接无效时,标注当前连接的discard状态为true
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(poolableConnection.holder);
continue;
}
}
}
}
getConnectionDirect是一个死循环,开启testWhileIdle后,判断如果当前连接超过了上一次的活性检测(默认空闲时间超过1分钟)时,进行活性检测
testWhileIdle与testOnBorrow不同的是,testWhileIdle会判断连接空闲时间大于阈值后再做活性检测,而testOnBorrow是在每次获取连接时都要进行活性检测,所以在并发量打的时候testWhileIdle会保证一定的可用性,而testOnBorrow增加获取连接的时间
总结
在dataSource中有大量的生产者/消费者模式
生产者
empt.await 生产者阻塞
empt.signal 生产者被唤醒,生产连接
消费者
notEmpty.await 消费者等待连接
notEmpty.signal 消费者唤醒,可以去使用连接
在getConnection时会大量使用这种模式进行线程间的切换,大大提升了获取连接的效率;
testWhileIdle是一种空闲时对连接的活性检测机制,在规定时间内最大限度的保证连接的可用性,失效连接及时丢弃,小于midIdle是唤醒生产者去创建连接;
testWhileIdle并不是在空闲的时候去检测连接的活性,而是在短时间内判断一个连接是否可用,因为有时候TCP断掉的时候会很久才会被发现。
在获取连接的时候,判断连接是不是很长时间没有用了,如果没有用了,那么去判断活性判断,在每次获取连接的时候,假如连接被动断掉了,是很长时间才会被发现的,所以这个机制能很快的发现已经断掉的连接。