Spring Boot集成Druid出现异常报错的原因及解决

网友投稿 1673 2023-01-16

Spring Boot集成Druid出现异常报错的原因及解决

Spring Boot集成Druid出现异常报错的原因及解决

Spring Boot集成Druid异常

在Spring Boot集成Druid项目中,发现错误日志中频繁的出现如下错误信息:

discard long time none received connection. , jdbcUrl : jdbc:mysql://******?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8, version : 1.2.3, lastPacketReceivedIdleMillis : 172675

经过排查发现是Druid版本导致的异常,在1.2.2及以前版本并未出现如此异常。而在其以上版本均存在此问题,下面就来分析一下异常原因及解决方案。

异常分析

首先上面的异常并不影响程序的正常运行,但作为程序员看到程序中不停的出现异常还是难以忍受的。所以还是要刨根问底的解决一下的。

跟踪堆栈信息会发现对应的异常是从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)。

原理追踪

在上述代码中,我们看到进入该业务逻辑是有前提条件的,也就是valid和isMySql变量同时为true。isMySql为true是必须的,我们使用的本身就是Mysql数据库。那么是否可以让valid为false呢?这样不就不会进入该业务处理了吗?

来看看valid的来源,还是在该方法的上面:

boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);

我们找到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;

}

throhttp://w 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 {

ALDZlHkjFdJdbcUtils.close(rs);

JdbcUtils.close(stmt);

}

}

我们可以看到上述方法中有三个返回的地方:第一个连接已关闭;第二个使用ping的形式进行检查;第三,使用select 1的方式进行检查。而使用ping的形式检查时,无论是否抛异常都会返回true。这里我们禁用该模式即可。

进入ping的业务逻辑主要靠变量usePingMethod来判断,追踪代码会发现在这里进行的设置:

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即可禁用该功能。

禁用Ping Method

找到了问题的根源,那么剩下的就是如何禁用了,通常有三种形式。

第一,在启动程序时在运行参数中增加:-Ddruid.mysql.usePingMethod=false。

第二,在Spring Boot项目中,可在启动类中添加如下静态代码快:

static {

System.setProperty("druid.mysql.usePingMethod","false");

}

第三,类文件配置。在项目的DruidConfig类中新增加:

/*

* 解决druid 日志报错:discard long time none received connection:xxx

* */

@PostConstruct

public void setProperties(){

SysALDZlHkjFdtem.setProperty("druid.mysql.usePingMethod","false");

}

至此,已可以成功关闭该功能,异常信息再也不会出现了。

为什么要清空空闲60秒以上的连接

猜测,阿里给数据库设置的数据库空闲等待时间是60秒,mysql数据库到了空闲等待时间将关闭空闲的连接,以提升数据库服务器的处理能力。

MySQL的默认空闲等待时间是8小时,就是「wait_timeout」的配置值。如果数据库主动关闭了空闲的连接,而连接池并不知道,还在使用这个连接,就会产生异常。

以上就是Spring Boot集成Druid出现异常报错的原因及解决的详细内容,更多关于Spring Boot集成Druid出现异常的资料请关注我们其它相关文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:小程序生态闭环图(小程序 生态)
下一篇:企业app开发定制类型(app定制开发公司有哪些)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~