数据库连接池 -Druid 源码学习(十)
作者:wjchenge
- 2022 年 5 月 20 日
本文字数:2726 字
阅读完需:约 9 分钟
1、简介
在本文中,我们将继续总结 testOnBorrow、testOnReturn、testWhileIdle 的异同。
2、环境
os-window10
druid-1.2.8
jdk-1.8.0_312
maven-3.8.1
复制代码
3、参数含义
连接的数据库为 MySql、Oracle、SQL_Server、PostgreSql 的情况下,validConnectionChecker 不为空。也就不会触发 validationQueryCheck()方法。
而配置了 testOnBorrow、testOnReturn、testWhileIdle 中其中一个则会执行以下的代码逻辑:
protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) {
String sqlFile = JdbcSqlStat.getContextSqlFile();
String sqlName = JdbcSqlStat.getContextSqlName();
if (sqlFile != null) {
JdbcSqlStat.setContextSqlFile(null);
}
if (sqlName != null) {
JdbcSqlStat.setContextSqlName(null);
}
try {
// validConnectionChecker 不为空执行的验证逻辑
if (validConnectionChecker != null) {
// 执行相应数据库实现类的验证逻辑
boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
long currentTimeMillis = System.currentTimeMillis();
if (holder != null) {
holder.lastValidTimeMillis = currentTimeMillis;
holder.lastExecTimeMillis = currentTimeMillis;
}
// 如果验证通过 并且是MySql数据库执行的逻辑
if (valid && isMySql) { // unexcepted branch
// 最后数据报文接受的时间毫秒数
long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
if (lastPacketReceivedTimeMs > 0) {
// MySql空闲毫秒数
long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
// System.out.println("lastPacketReceivedTimeMs = " + lastPacketReceivedTimeMs);
// System.out.println("lastActiveTimeMillis = " + holder.lastActiveTimeMillis);
if (lastPacketReceivedTimeMs > 0 //
&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
// 将该连接从数据库中丢弃掉
discardConnection(holder);
String errorMsg = "discard long time none received connection. "
+ ", jdbcUrl : " + jdbcUrl
+ ", version : " + VERSION.getVersionNumber()
+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
LOG.warn(errorMsg);
return false;
}
}
}
if (valid && onFatalError) {
lock.lock();
try {
if (onFatalError) {
onFatalError = false;
}
} finally {
lock.unlock();
}
}
return valid;
}
if (conn.isClosed()) {
return false;
}
if (null == validationQuery) {
return true;
}
Statement stmt = null;
ResultSet rset = null;
try {
stmt = conn.createStatement();
if (getValidationQueryTimeout() > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
// validConnectionChecker为空的情况下的验证连接可用
rset = stmt.executeQuery(validationQuery);
if (!rset.next()) {
return false;
}
} finally {
JdbcUtils.close(rset);
JdbcUtils.close(stmt);
}
if (onFatalError) {
lock.lock();
try {
if (onFatalError) {
onFatalError = false;
}
} finally {
lock.unlock();
}
}
return true;
} catch (Throwable ex) {
// skip
return false;
} finally {
if (sqlFile != null) {
JdbcSqlStat.setContextSqlFile(sqlFile);
}
if (sqlName != null) {
JdbcSqlStat.setContextSqlName(sqlName);
}
}
}
复制代码
而 MySql 数据库的实现类的源码如下,关键代码就是 validationQuery 为空的情况下会赋值为"SELECT 1"
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
if (conn.isClosed()) {
return false;
}
if (usePingMethod) {
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
// validateQuery 参数为null的情况下默认赋值 SELECT 1
String query = validateQuery;
if (validateQuery == null || validateQuery.isEmpty()) {
query = DEFAULT_VALIDATION_QUERY;
}
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
rs = stmt.executeQuery(query);
return true;
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
复制代码
4、其他差异
testOnBorrow:在获取连接的时候就能检测数据库的异常信息。
testOnReturn、testWhileIdle:都不能在获取连接的第一时间检查连接的可用性,拿到连接后在执行 sql 语句时会报异常。
测试发现应用与数据库的连接断开的情况下,应用会报错,但网络恢复后配置 testOnBorrow 会重新获取到可用连接。而配置 testOnReturn、testWhileIdle 参数则会在执行 sql 时发生不可恢复的异常,导致测试类终止运行,网络恢复后系统也不可用。
划线
评论
复制
发布于: 刚刚阅读数: 3
版权声明: 本文为 InfoQ 作者【wjchenge】的原创文章。
原文链接:【http://xie.infoq.cn/article/abf22b57733e97bc705c6e66a】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
wjchenge
关注
还未添加个人签名 2018.07.27 加入
还未添加个人简介
评论