add double check for mysql testWhileIdle.

This commit is contained in:
wenshao
2017-11-01 12:24:22 +08:00
parent d1189cefe6
commit 616ec0d431
5 changed files with 173 additions and 35 deletions
@@ -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<Long> waitNanosLocal = new ThreadLocal<Long>();
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());
@@ -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();
}
}
@@ -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();
@@ -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();
@@ -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);
}
}
}