目录
第6篇中,分析了如何获取连接,那获取连接后,Druid 是如何管理这些连接的呢?
可以先给结论,是通过几个关键参数来控制的
testOnBorrow
testOnReturn
testWhileIdle
testOnBorrow
首先看 testOnBorrow,从字面理解,是从连接池中获取连接时,要测试连接是否可用。下面是代码中使用这个属性的几处地方:
1179 和 1193 行,是在 validationQueryCheck() 方法中使用的,这个方法是判断是否正确设置了验证连接可用性相关的参数,即如下参数:
testOnBorrow
testOnReturn
testWhileIdle
validConnectionChecker
validationQuery
高亮的 1452 行是个关键,在 DruidDataSource#getConnectionDirect(long maxWaitMillis) 方法中:
if (testOnBorrow) {
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(poolableConnection.holder);
continue;
}
}
可以看到,如果设置了这个参数为 true,就通过 testConnectionInternal 这个方法验证连接是否有效,如果连接无效,通过 discardConnection 方法抛弃连接。
testWhileIdle
下面在看下如果设置为 false 的逻辑,同时里面引出了 testWhileIdle 这个属性(使用空闲时间验证连接可用性)。
} else {
if (poolableConnection.conn.isClosed()) {
discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
continue;
}
// 使用空闲时间验证连接可用性
if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
// 当前时间戳
long currentTimeMillis = System.currentTimeMillis();
// 最近一次使用这个连接的时间戳
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
// 最近一次执行SQL的时间戳
long lastExecTimeMillis = holder.lastExecTimeMillis;
// 最近一次保活时间戳
long lastKeepTimeMillis = holder.lastKeepTimeMillis;
// 如果要检查执行SQL时间,并且执行SQL和使用连接的时间戳不相等时
if (checkExecuteTime
&& lastExecTimeMillis != lastActiveTimeMillis) {
// 把最近一次执行SQL时间戳赋值给最近一次使用连接的时间戳
lastActiveTimeMillis = lastExecTimeMillis;
}
// 如果最近一次保活时间戳比最近一次使用连接时间戳更新,赋值
if (lastKeepTimeMillis > lastActiveTimeMillis) {
lastActiveTimeMillis = lastKeepTimeMillis;
}
// 连接空闲时间
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
// 剔除连接最小间隔时间
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
if (timeBetweenEvictionRunsMillis <= 0) {
// 默认 60s
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
// 空闲时间比剔除最小时间大了
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(poolableConnection.holder);
continue;
}
}
}
}
通过分析可知,只有这个连接的空闲时间超过了剔除最小时间时,才会对连接进行检测,所以不会像获取或归还连接时就进行测试有性能影响,所以是推荐打开的属性。
testOnReturn
这个属性就是控制在归还连接给连接池时,是否做连接可用测试。
可以看到,在 DruidDataSource 的 1955 行使用了这个属性,为 true 时,对连接进行可用测试,如果已经断开连接了,就关闭连接。
final boolean testOnReturn = this.testOnReturn;
...
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;
}
}
以上代码是在如下方法中的:
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException
recycle(DruidPooledConnection) 被调用的地方只有如下方法:
public void recycle() throws SQLException
被3个地方调用,可以知道都是在关闭连接时进行的调用。
结论
建议关闭 testOnBorrow 和 testOnReturn,打开 testWhileIdle。