diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 35e39dd034..dfd4300706 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -90,11 +90,7 @@ import com.alibaba.druid.stat.JdbcSqlStat; import com.alibaba.druid.stat.JdbcSqlStatValue; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; -import com.alibaba.druid.util.JMXUtils; -import com.alibaba.druid.util.JdbcConstants; -import com.alibaba.druid.util.JdbcUtils; -import com.alibaba.druid.util.StringUtils; -import com.alibaba.druid.util.Utils; +import com.alibaba.druid.util.*; import com.alibaba.druid.wall.WallFilter; import com.alibaba.druid.wall.WallProviderStatValue; @@ -169,14 +165,11 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat private boolean mbeanRegistered = false; public static ThreadLocal waitNanosLocal = new ThreadLocal(); - private boolean logDifferentThread = true; - private volatile boolean keepAlive = false; - private boolean asyncInit = false; - protected boolean killWhenSocketReadTimeout = false; + protected boolean killWhenSocketReadTimeout = false; public DruidDataSource(){ this(false); @@ -224,13 +217,13 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat { Boolean value = getBoolean(properties, "druid.testWhileIdle"); if (value != null) { - this.setTestWhileIdle(value); + this.testWhileIdle = value; } } { Boolean value = getBoolean(properties, "druid.testOnBorrow"); if (value != null) { - this.setTestOnBorrow(value); + this.testOnBorrow = value; } } { @@ -1043,7 +1036,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } private void validationQueryCheck() { - if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) { + if (!(testOnBorrow || testOnReturn || testWhileIdle)) { return; } @@ -1051,21 +1044,21 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat return; } - if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) { + if (this.validationQuery != null && this.validationQuery.length() > 0) { return; } String errorMessage = ""; - if (isTestOnBorrow()) { + if (testOnBorrow) { errorMessage += "testOnBorrow is true, "; } - if (isTestOnReturn()) { + if (testOnReturn) { errorMessage += "testOnReturn is true, "; } - if (isTestWhileIdle()) { + if (testWhileIdle) { errorMessage += "testWhileIdle is true, "; } @@ -1269,15 +1262,19 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } if (testWhileIdle) { - final long currentTimeMillis = System.currentTimeMillis(); - final long lastActiveTimeMillis = poolableConnection.holder.lastActiveTimeMillis; - final long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long currentTimeMillis = System.currentTimeMillis(); + long lastActiveTimeMillis = poolableConnection.holder.lastActiveTimeMillis; + long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis; + if (timeBetweenEvictionRunsMillis <= 0) { timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; } - if (idleMillis >= timeBetweenEvictionRunsMillis) { + if (idleMillis >= timeBetweenEvictionRunsMillis + || idleMillis < 0 // unexcepted branch + ) { boolean validate = testConnectionInternal(poolableConnection.conn); if (!validate) { if (LOG.isDebugEnabled()) { @@ -1287,6 +1284,17 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat discardConnection(realConnection); continue; } + + if (JdbcConstants.MYSQL_DRIVER.equals(driverClass)) { // unexcepted branch + long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(realConnection); + long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs; + if (lastPacketReceivedTimeMs > 0 // + && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { + discardConnection(realConnection); + LOG.warn("discard long time none received connection. millis : " + mysqlIdleMillis); + continue; + } + } } } } @@ -2029,9 +2037,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName()); value.setExceptionSorterClassName(this.getExceptionSorterClassName()); - value.setTestOnBorrow(this.isTestOnBorrow()); - value.setTestOnReturn(this.isTestOnReturn()); - value.setTestWhileIdle(this.isTestWhileIdle()); + value.setTestOnBorrow(this.testOnBorrow); + value.setTestOnReturn(this.testOnReturn); + value.setTestWhileIdle(this.testWhileIdle); value.setDefaultAutoCommit(this.isDefaultAutoCommit()); @@ -2138,7 +2146,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat // addLast lock.lock(); try { - if (closed) { + if (closed || closing) { + createTaskCount--; return; } @@ -2198,6 +2207,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } this.errorCount = 0; // reset errorCount + if (closing || closed) { + createTaskCount--; + return; + } createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS); return; } @@ -2228,6 +2241,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } this.errorCount = 0; // reset errorCount + if (closing || closed) { + createTaskCount--; + return; + } createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS); return; } @@ -2247,6 +2264,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat // unknow fatal exception setFailContinuous(true); break; + } catch (Throwable e) { + LOG.error("create connection unexecpted error.", e); + break; } if (physicalConnection == null) { @@ -2685,7 +2705,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } if (validate) { - holer.setLastActiveTimeMillis(System.currentTimeMillis()); + holer.lastActiveTimeMillis = System.currentTimeMillis(); put(holer); } else { JdbcUtils.close(connection); @@ -2964,7 +2984,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat // 15 - 19 map.put("TestOnReturn", this.isTestOnReturn()); - map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); + map.put("MinEvictableIdleTimeMillis", this.minEvictableIdleTimeMillis); map.put("ConnectErrorCount", this.getConnectErrorCount()); map.put("CreateTimespanMillis", this.getCreateTimespanMillis()); map.put("DbType", this.dbType); @@ -3110,8 +3130,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat dataMap.put("MaxWaitThreadCount", this.getMaxWaitThreadCount()); dataMap.put("PoolPreparedStatements", this.isPoolPreparedStatements()); dataMap.put("MaxPoolPreparedStatementPerConnectionSize", this.getMaxPoolPreparedStatementPerConnectionSize()); - dataMap.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); - dataMap.put("MaxEvictableIdleTimeMillis", this.getMaxEvictableIdleTimeMillis()); + dataMap.put("MinEvictableIdleTimeMillis", this.minEvictableIdleTimeMillis); + dataMap.put("MaxEvictableIdleTimeMillis", this.maxEvictableIdleTimeMillis); dataMap.put("LogDifferentThread", isLogDifferentThread()); dataMap.put("RecycleErrorCount", getRecycleErrorCount()); diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 3a57792949..24f6ad850a 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -1206,16 +1206,16 @@ public class DruidPooledConnection extends PoolableWrapper implements javax.sql. final void beforeExecute() { final DruidConnectionHolder holder = this.holder; - if (holder != null && holder.getDataSource().isRemoveAbandoned()) { + if (holder != null && holder.dataSource.removeAbandoned) { running = true; } } final void afterExecute() { final DruidConnectionHolder holder = this.holder; - if (holder != null && holder.getDataSource().isRemoveAbandoned()) { + if (holder != null && holder.dataSource.removeAbandoned) { running = false; - holder.setLastActiveTimeMillis(System.currentTimeMillis()); + holder.lastActiveTimeMillis = System.currentTimeMillis(); } } diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledPreparedStatement.java b/src/main/java/com/alibaba/druid/pool/DruidPooledPreparedStatement.java index 28f7448285..29c26dbfed 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledPreparedStatement.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledPreparedStatement.java @@ -236,6 +236,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement return poolableResultSet; } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -253,6 +255,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement try { return stmt.executeUpdate(); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -493,6 +497,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement try { return stmt.execute(); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -559,6 +565,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement try { return stmt.executeBatch(); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java index 64ac35805a..093a4435f5 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.pool; +import com.alibaba.druid.VERSION; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcConstants; @@ -225,6 +226,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { return poolableResultSet; } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -242,12 +245,33 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.executeUpdate(sql); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); } } + protected final void errorCheck(Throwable t) { + String errorClassName = t.getClass().getName(); + if (errorClassName.endsWith(".CommunicationsException") + && conn.holder != null + && conn.holder.dataSource.testWhileIdle) + { + DruidConnectionHolder holder = conn.holder; + DruidAbstractDataSource dataSource = holder.dataSource; + + long lastActiveTimeMillis = holder.lastActiveTimeMillis; + long idleMillis = System.currentTimeMillis() - lastActiveTimeMillis; + + LOG.error("CommunicationsException, druid version " + VERSION.getVersionNumber() + + ", jdbcUrl : " + dataSource.jdbcUrl + + ", testWhileIdle " + dataSource.testWhileIdle + + ", idle millis " + idleMillis); + } + } + @Override public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { checkOpen(); @@ -259,6 +283,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.executeUpdate(sql, autoGeneratedKeys); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -276,6 +302,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.executeUpdate(sql, columnIndexes); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -293,6 +321,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.executeUpdate(sql, columnNames); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -310,6 +340,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.execute(sql, autoGeneratedKeys); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -327,6 +359,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.execute(sql, columnIndexes); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -344,6 +378,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.execute(sql, columnNames); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); @@ -496,6 +532,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.execute(sql); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } } @@ -653,6 +691,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { try { return stmt.executeBatch(); } catch (Throwable t) { + errorCheck(t); + throw checkException(t); } finally { conn.afterExecute(); diff --git a/src/main/java/com/alibaba/druid/util/MySqlUtils.java b/src/main/java/com/alibaba/druid/util/MySqlUtils.java index 1ee13964c6..15537564bd 100644 --- a/src/main/java/com/alibaba/druid/util/MySqlUtils.java +++ b/src/main/java/com/alibaba/druid/util/MySqlUtils.java @@ -16,15 +16,11 @@ package com.alibaba.druid.util; import com.alibaba.druid.sql.SQLUtils; -import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.mysql.jdbc.ConnectionImpl; -import com.mysql.jdbc.MySQLConnection; -import com.mysql.jdbc.MysqlIO; import javax.sql.XAConnection; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.*; @@ -234,4 +230,78 @@ public class MySqlUtils { } return SQLUtils.toSQLString(stmtList, JdbcConstants.MYSQL); } + + private static Class class_connectionImpl = null; + private static boolean class_connectionImpl_Error = false; + private static Method method_getIO = null; + private static boolean method_getIO_error = false; + private static Class class_MysqlIO = null; + private static boolean class_MysqlIO_Error = false; + private static Method method_getLastPacketReceivedTimeMs = null; + private static boolean method_getLastPacketReceivedTimeMs_error = false; + + public static long getLastPacketReceivedTimeMs(Connection conn) throws SQLException { + if (class_connectionImpl == null && !class_connectionImpl_Error) { + try { + class_connectionImpl = Utils.loadClass("com.mysql.jdbc.ConnectionImpl"); + } catch (Throwable error){ + class_connectionImpl_Error = true; + } + } + + if (class_connectionImpl == null) { + return -1; + } + + if (method_getIO == null && !method_getIO_error) { + try { + method_getIO = class_connectionImpl.getMethod("getIO"); + } catch (Throwable error){ + method_getIO_error = true; + } + } + + if (method_getIO == null) { + return -1; + } + + if (class_MysqlIO == null && !class_MysqlIO_Error) { + try { + class_MysqlIO = Utils.loadClass("com.mysql.jdbc.MysqlIO"); + } catch (Throwable error){ + class_MysqlIO_Error = true; + } + } + + if (class_MysqlIO == null) { + return -1; + } + + if (method_getLastPacketReceivedTimeMs == null && !method_getLastPacketReceivedTimeMs_error) { + try { + Method method = class_MysqlIO.getDeclaredMethod("getLastPacketReceivedTimeMs"); + method.setAccessible(true); + method_getLastPacketReceivedTimeMs = method; + } catch (Throwable error){ + method_getLastPacketReceivedTimeMs_error = true; + } + } + + if (method_getLastPacketReceivedTimeMs == null) { + return -1; + } + + try { + Object connImpl = conn.unwrap(class_connectionImpl); + Object mysqlio = method_getIO.invoke(connImpl); + Long ms = (Long) method_getLastPacketReceivedTimeMs.invoke(mysqlio); + return ms.longValue(); + } catch (IllegalArgumentException e) { + throw new SQLException("getLastPacketReceivedTimeMs error", e); + } catch (IllegalAccessException e) { + throw new SQLException("getLastPacketReceivedTimeMs error", e); + } catch (InvocationTargetException e) { + throw new SQLException("getLastPacketReceivedTimeMs error", e); + } + } }