druid升级1.0.26遇到的连接闲置断开的问题
2016-11-08 23:40
429 查看
1. 背景
之前处理了一个mybatis的bug(详见之前的文章),在给系统升级mybatis的时候,想顺便把所有系统的druid也升级了,从版本(0.2.18~1.0.18不等)升级到统一的最新版本(1.0.26)。
但是在beta测试期间,遇到如下异常:
复现的方法是程序启动后,访问一些需要查库的功能,然后闲置直至超过mysql配置的wait_timeout时间(默认8小时,可以改小,如3分钟,方便复现),再访问需要查库的资源,就出现这个问题了。
2.相关配置
代码封装了一个工厂,对druid进行了统一的配置。
其中和连接相关的配置只有:
url中的autoReconnect=true(但这个看网上说在mysql 5.x以上不管用了,没有具体考证,不敢乱说)
testWhileIdle=true这两条。
3. 代码分析
查询时会获取连接(DruidDataSource.getConnectionDirect),在内部会检测连接的状态(DruidDataSource.testConnectionInternal),检测时使用validConnectionChecker.isValidConnection。
下面比较不存在这个问题的1.0.18版本和存在问题的1.0.26版本,通过跟踪代码的执行,发现两个版本在该部分使用的判断逻辑不同,如下:
3.1 1.0.18版本
3.2 1.0.26版本
可见在1.0.26版本中,需要配置validationQuery,否则一直返回true(连接可用),当达到mysql的wait_timeout后,就出问题了(druid认为连接还有效,但实际连接已经断开了)。
再翻看代码,发现1.0.26版本中也是有MySqlValidConnectionChecker啊,为什么没用上呢?原来是方法名改了,没重载上=。=
往前看了几个版本,发现从1.0.21版本就是这样了。
目前还没有理解这么做的原因是什么,是因为mysql版本太多,这个功能不通用?还是当时开发的时候名字改错了?
提了个issue,等大大们回复了。
————————————————————————————————————————————
最新进展:大大们已经修复了,更新到1.0.27版本即可。
之前处理了一个mybatis的bug(详见之前的文章),在给系统升级mybatis的时候,想顺便把所有系统的druid也升级了,从版本(0.2.18~1.0.18不等)升级到统一的最新版本(1.0.26)。
但是在beta测试期间,遇到如下异常:
ERROR com.alibaba.druid.pool.DruidDataSource:1243 discard connection com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 1,463,838 milliseconds ago. The last packet sent successfully to the server was 1,463,839 mill iseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configure d values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.7.0_45] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) ~[na:1.7.0_45] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.7.0_45] at java.lang.reflect.Constructor.newInstance(Constructor.java:526) ~[na:1.7.0_45] at com.mysql.jdbc.Util.handleNewInstance(Util.java:408) ~[mysql-connector-java-5.1.32.jar:na] at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1137) ~[mysql-connector-java-5.1.32.jar:na]
复现的方法是程序启动后,访问一些需要查库的功能,然后闲置直至超过mysql配置的wait_timeout时间(默认8小时,可以改小,如3分钟,方便复现),再访问需要查库的资源,就出现这个问题了。
2.相关配置
代码封装了一个工厂,对druid进行了统一的配置。
其中和连接相关的配置只有:
url中的autoReconnect=true(但这个看网上说在mysql 5.x以上不管用了,没有具体考证,不敢乱说)
testWhileIdle=true这两条。
3. 代码分析
查询时会获取连接(DruidDataSource.getConnectionDirect),在内部会检测连接的状态(DruidDataSource.testConnectionInternal),检测时使用validConnectionChecker.isValidConnection。
下面比较不存在这个问题的1.0.18版本和存在问题的1.0.26版本,通过跟踪代码的执行,发现两个版本在该部分使用的判断逻辑不同,如下:
3.1 1.0.18版本
/* * Copyright 1999-2101 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.druid.pool.vendor; import com.alibaba.druid.pool.DruidPooledConnection; import com.alibaba.druid.pool.ValidConnectionChecker; import com.alibaba.druid.pool.ValidConnectionCheckerAdapter; import com.alibaba.druid.proxy.jdbc.ConnectionProxy; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.util.Utils; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class MySqlValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable { public static final int DEFAULT_VALIDATION_QUERY_TIMEOUT = 1000; private static final long serialVersionUID = 1L; private static final Log LOG = LogFactory.getLog(MySqlValidConnectionChecker.class); private Class<?> clazz; private Method ping; private boolean usePingMethod = false; public MySqlValidConnectionChecker(){ try { clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection"); ping = clazz.getMethod("pingInternal", boolean.class, int.class); if (ping != null) { usePingMethod = true; } } catch (Exception e) { LOG.warn("Cannot resolve com.mysq.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e); } configFromProperties(System.getProperties()); } @Override 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); } } public boolean isUsePingMethod() { return usePingMethod; } public void setUsePingMethod(boolean usePingMethod) { this.usePingMethod = usePingMethod; } public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) { try { if (conn.isClosed()) { return false; } } catch (SQLException ex) { // skip 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); return true; } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof SQLException) { return false; } LOG.warn("Unexpected error in ping", e); return false; } catch (Exception e) { LOG.warn("Unexpected error in ping", e); return false; } } } Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(); if (validationQueryTimeout > 0) { stmt.setQueryTimeout(validationQueryTimeout); } rs = stmt.executeQuery(validateQuery); return true; } catch (SQLException e) { return false; } catch (Exception e) { LOG.warn("Unexpected error in ping", e); return false; } finally { JdbcUtils.close(rs); JdbcUtils.close(stmt); } } }
3.2 1.0.26版本
/* * Copyright 1999-2101 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.druid.pool; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; import com.alibaba.druid.util.JdbcUtils; /** * @author wenshao [szujobs@hotmail.com] * @since 0.2.21 */ public class ValidConnectionCheckerAdapter implements ValidConnectionChecker { @Override public boolean isValidConnection(Connection conn, String query, int validationQueryTimeout) throws Exception { if (query == null || query.length() == 0) { return true; } 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); } } @Override public void configFromProperties(Properties properties) { } }
可见在1.0.26版本中,需要配置validationQuery,否则一直返回true(连接可用),当达到mysql的wait_timeout后,就出问题了(druid认为连接还有效,但实际连接已经断开了)。
再翻看代码,发现1.0.26版本中也是有MySqlValidConnectionChecker啊,为什么没用上呢?原来是方法名改了,没重载上=。=
/* * Copyright 1999-2101 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.druid.pool.vendor; import java.io.Serializable; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; import com.alibaba.druid.pool.DruidPooledConnection; import com.alibaba.druid.pool.ValidConnectionChecker; import com.alibaba.druid.pool.ValidConnectionCheckerAdapter; import com.alibaba.druid.proxy.jdbc.ConnectionProxy; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.util.Utils; public class MySqlValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable { public static final int DEFAULT_VALIDATION_QUERY_TIMEOUT = 1000; private static final long serialVersionUID = 1L; private static final Log LOG = LogFactory.getLog(MySqlValidConnectionChecker.class); private Class<?> clazz; private Method ping; private boolean usePingMethod = false; public MySqlValidConnectionChecker(){ try { clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection"); if (clazz == null) { clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl"); } if (clazz != null) { ping = clazz.getMethod("pingInternal", boolean.class, int.class); } if (ping != null) { usePingMethod = true; } } catch (Exception e) { LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e); } configFromProperties(System.getProperties()); } @Override 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); } } public boolean isUsePingMethod() { return usePingMethod; } public void setUsePingMethod(boolean usePingMethod) { this.usePingMethod = usePingMethod; } public boolean validConnection(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; } ping.invoke(conn, true, validationQueryTimeout * 1000); return true; } } Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(); if (validationQueryTimeout > 0) { stmt.setQueryTimeout(validationQueryTimeout); } rs = stmt.executeQuery(validateQuery); return true; } finally { JdbcUtils.close(rs); JdbcUtils.close(stmt); } } }
往前看了几个版本,发现从1.0.21版本就是这样了。
目前还没有理解这么做的原因是什么,是因为mysql版本太多,这个功能不通用?还是当时开发的时候名字改错了?
提了个issue,等大大们回复了。
————————————————————————————————————————————
最新进展:大大们已经修复了,更新到1.0.27版本即可。
相关文章推荐
- 解决ssh登录后闲置时间过长而断开连接的问题
- Xmpp遇到的问题:openfire中发送某些特殊字符会断开xmpp连接的问题(3)
- 升级到ActiveSync 4.5后“可能”遇到的网络连接问题,以及解决方法
- 升级到ActiveSync 4.5后“可能”遇到的网络连接问题,以及解决方法 (转)
- mysql超时设置的问题,如果连接闲置8小时 (8小时内没有进行数据库操作), mysql就会自动断开连接, 要重启tomcat
- 使用ASP开发项目时遇到的连接数据库的性能问题。
- TortoiseSVN从1.2.6升级到1.3.2过程中遇到的问题
- TortoiseSVN从1.2.6升级到1.3.2过程中遇到的问题
- 遇到一个问题,在rcp里不能连接上去
- 升级到MySQL 5.0.17一定遇到的四个问题
- ASP.NET和SQL SERVER连接中遇到的问题
- VC连接ORACLE遇到的问题
- 静态库编译和连接遇到的奇怪问题
- vs2003升级到vs2005遇到的问题
- 解决 c3p0 和 MySQL 集成情况下,连接长时间闲置后重新使用时报错的问题
- 用JSP连接SQL时遇到的问题---求高手解答
- 从 .net 1.1 升级到 2.0过程中遇到的问题及解决方法
- 使用WebServices时候遇到“基础连接以关闭”的问题
- 从 CCS 1.1 升级到 CS 2.0 所遇到的问题和部分解决方法
- Pligg beta9.7升级到pligg beta 9.8过程遇到的(中文支持)问题解决