写点什么

数据库连接池 -Druid 源码学习(十)

作者:wjchenge
  • 2022 年 5 月 20 日
  • 本文字数:2726 字

    阅读完需:约 9 分钟

1、简介

在本文中,我们将继续总结 testOnBorrow、testOnReturn、testWhileIdle 的异同。

前置内容:数据库连接池 -Druid 源码学习(九)

2、环境

os-window10druid-1.2.8jdk-1.8.0_312maven-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
用户头像

wjchenge

关注

还未添加个人签名 2018.07.27 加入

还未添加个人简介

评论

发布
暂无评论
数据库连接池 -Druid 源码学习(十)_Druid_wjchenge_InfoQ写作社区