问题:
在用在Spring Boot集成Druid项目中,发现日志出现如下错误信息:
discard long time none received connection.
jdbcUrl : jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=CTT,
jdbcUrl : jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=CTT,
lastPacketReceivedIdleMillis : 31990
虽然并不影响程序正常运行,但有错误终归是不好的
分析:
从com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal抛出的异常点入看
if (valid && isMySql) { // unexcepted branch
long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
if (lastPacketReceivedTimeMs > 0) {
long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
if (lastPacketReceivedTimeMs > 0 //
&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
discardConnection(holder);
String errorMsg = "discard long time none received connection. "
+ ", jdbcUrl : " + jdbcUrl
+ ", jdbcUrl : " + jdbcUrl
+ ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis;
LOG.error(errorMsg);
return false;
}
}
}
上述代码中,MySqlUtils.getLastPacketReceivedTimeMs(conn) 是获取上一次使用的时间,mysqlIdleMillis 就是计算出来空闲的时间,而timeBetweenEvictionRunsMillis 是常量60秒。也就是说如果连接空闲了60秒以上,那就会discardConnection(holder) 丢弃这个旧连接并顺带打印了一个日志LOG.warn(errorMsg)。
解决:
1.可以看到valid && isMySql都为true就会进入该方法,而isMySql是必然为true的,因为使用的就是MySQL数据。那就是让valid为false,继续点进去valid看方法来源
boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
3.继续进入isValidConnection方法看看,找到validConnectionChecker的Mysql实现子类MySqlValidConnectionChecker,该类中对isValidConnection的实现如下
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; } } 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); }
}
我们可以看到上述方法中有三个返回的地方:第一个连接已关闭;第二个使用ping的形式进行检查;第三,使用select 1的方式进行检查。而使用ping的形式检查时,无论是否抛异常都会返回true。这里我们禁用该模式即可。
4.然后进入到ping的方法
public void configFromProperties(Properties properties) {
String property = properties.getProperty("druid.mysql.usePingMethod");
if ("true".equals(property)) {
setUsePingMethod(true);
} else if ("false".equals(property)) {
setUsePingMethod(false);
}
}
可以得出只要把druid.mysql.usePingMethod设置为false即可禁用该功能
5.找到druid的配置
添加上use-ping-method: false就好了
原因:
要设置空闲等待时间60秒后没有连接就断开大概是因为优化数据库处理能力而MySQL的默认空闲等待时间是8小时。如果数据库主动关闭了空闲的连接,而连接池还在使用这个连接,就会产生异常。
参考文章:
(1条消息) Spring Boot集成Druid异常discard long time none received connection._程序新视界的博客-CSDN博客_discard long time none received connection.
版权归原作者 kkoneone11 所有, 如有侵权,请联系我们删除。