(一)使用Presto中的Druid报错:recyle error 和 abandon connection 来深入研究Druid的底层实现原理
(1)本文主要是使用Druid作为Presto的连接池,所遇到的问题,以及的错误和解决方法
先看一下我的Druid的配置
- druidDataSource.setTestWhileIdle(true);
- druidDataSource.setTestOnBorrow(true);
- druidDataSource.setTestOnReturn(false);
- druidDataSource.setQueryTimeout(15);
- druidDataSource.setMaxActive(20); 配置Druid的连接数的峰值
- druidDataSource.setMaxWait(6000); 从连接池获取连接的超时等待时间,单位毫秒
- druidDataSource.setMinIdle(1); 设置池子中的固定的空闲连接数
- druidDataSource.setKeepAlive(true);
- druidDataSource.setTimeBetweenEvictionRunsMillis(30000);Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
- druidDataSource.setMinEvictableIdleTimeMillis(60000);//配置一个连接在池中最小生存的时间,单位是毫秒;即最多允许一个连接空闲多长时间,超出此时间的连接,会被destroy线程关闭
druidDataSource.setMaxEvictableIdleTimeMillis(90000);配置一个连接在池子中的最大存时间
druidDataSource.setRemoveAbandoned(true);是否强制关闭连接时长大于removeAbandonedTimeoutMillis的连接
druidDataSource.setRemoveAbandonedTimeout(180); 这个是强制关闭的时间值限定单位是秒
//这里我开启了Druid的超时回收连接机制
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(5);单位秒
druidDataSource.setLogAbandoned(true); 这个如果打开的话,就可以看到Druid在回收的连接的时候报错信息:
首先会出现recyle error 的原因是因为我咋Druid的配置中开启了Druid的超时回收连接机制,Druid的内部回收线程会去回收Connection对象,所以会出现recyle error的报错,但是其内部的根本原因是因为Druid再回收的过程中遇到了错误,并且把日志打印出来了(这个是Druid默认打开的),这个Druid打开的目的就是为了告诉你,你的应用中有一些Connection回收的过程中失败,但是并不影响Druid真正把这个Connection给强制回收,但是此时你的程序肯定是有问题的。
解决方法:
- 要么是直接加上这个配置,把Druid打印回收Connection的错误日志开关给关闭,但是这样指标不治本,druidDataSource.setLogAbandoned(false);
- 自己去仔细的排查程序中可能会出现的Connection回收失败的问题
(2)去排查自己程序中是否存在Connection回收失败的问题
/**
* 获取Druid连接对象
*
* @return
*/
public static DruidPooledConnection druidPooledConnection = null;
public static synchronized PrestoConnection getConnection() {
PrestoConnection conn = null;
try {
druidPooledConnection = druidDataSource.getConnection();
conn = (PrestoConnection) druidPooledConnection.getConnection();
} catch (SQLException e) {
log.error("创建Connection失败异常信息 msg={}", " 原因", e);
}
return conn;
}
看一下我业务中关闭connection的时候,其实关闭的是PrestoConnection,但是其实应该是关闭DruidPooledConnection。业务中的错误代码是这样的:
try {
if (!resultSet.isClosed() && !statement.isClosed()){
resultSet.close();
statement.close();
log.info(" Close the ResultSet success: {}, Close the statement success: {}", resultSet.isClosed(), statement.isClosed());
}
if (!prestoConnection.isClosed()) {
prestoConnection.close();
log.info(" Close the prestoConnection success: {}", prestoConnection.isClosed());
}
} catch (SQLException e) {
log.error("关闭prestoConnection或者Statement失败异常信息 msg={}", "原因", e);
e.printStackTrace();
}
把上面的关闭prestoConnection代码如果改成关闭druidPooledConnection就好了
druidPooledConnection.close();
经过一系列的问题排查,最终定位,发现其真正的问题是因为我的DruidPooledConnection没有关闭(这个DruidPooledConnection是直接从DruidDataSource中获取到的),但是我在程序中每次查询完业务逻辑之后直接把我的PrestoConnection对象的连接给关闭了,关闭的不是我应该关闭的connection对象,所以Druid回收线程一直报错,说明Connection泄漏了
(3)总结底层原理的原因
首先因为不仅实现了Connection接口 而且还实现了PooledConnection 接口,但是PrestoConnection只实现了Connection接口,而我在使用的时候直接把PooledConnection强制转化为PrestoConnection了,那么其实PooledConnection这个相当于是我的逻辑连接,而每次我直接在调用PrestoConnection.close()的时候,其实关闭的物理连接(因为PrestoConnection并没有实现PooledConnection这个接口,所以根据不具有Druid中的逻辑连接特性)
- PrestoConnection conn = (PrestoConnection) druidPooledConnection.getConnection();
- DruidPooledConnection extends PoolableWrapper implements javax.sql.PooledConnection, Connection
- public class PrestoConnection implements Connection
更多推荐
所有评论(0)