diff --git a/src/main/java/com/alibaba/druid/mock/MockConnection.java b/src/main/java/com/alibaba/druid/mock/MockConnection.java index 9c8f655a27..fbd2bb2b4e 100644 --- a/src/main/java/com/alibaba/druid/mock/MockConnection.java +++ b/src/main/java/com/alibaba/druid/mock/MockConnection.java @@ -37,6 +37,7 @@ public class MockConnection extends ConnectionBase implements Connection { private final long createdTimeMillis = System.currentTimeMillis(); private long lastActiveTimeMillis = System.currentTimeMillis(); + private int networkTimeout; private SQLException error; @@ -455,11 +456,11 @@ public class MockConnection extends ConnectionBase implements Connection { } public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { - throw new SQLFeatureNotSupportedException(); + this.networkTimeout = milliseconds; } public int getNetworkTimeout() throws SQLException { - throw new SQLFeatureNotSupportedException(); + return networkTimeout; } @Override diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 6cde51a8bd..43c075b498 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -43,6 +43,7 @@ import java.io.Serializable; import java.sql.*; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; @@ -70,6 +71,8 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = 60 * 1000L; public static final long DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS = 500; public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; + public static final int DEFAULT_TIME_CONNECT_TIMEOUT_MILLIS = 10_000; + public static final int DEFAULT_TIME_SOCKET_TIMEOUT_MILLIS = 10_000; public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L; public static final long DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 60L * 7; @@ -119,8 +122,11 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected Driver driver; - protected volatile int queryTimeout; - protected volatile int transactionQueryTimeout; + protected volatile int connectTimeout = DEFAULT_TIME_CONNECT_TIMEOUT_MILLIS; // milliSeconds + protected volatile int socketTimeout = DEFAULT_TIME_SOCKET_TIMEOUT_MILLIS; // milliSeconds + + protected volatile int queryTimeout; // seconds + protected volatile int transactionQueryTimeout; // seconds protected long createTimespan; @@ -251,6 +257,8 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected volatile long failContinuousTimeMillis; protected ScheduledExecutorService destroyScheduler; protected ScheduledExecutorService createScheduler; + protected Executor netTimeoutExecutor; + protected volatile boolean netTimeoutError; static final AtomicLongFieldUpdater failContinuousTimeMillisUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "failContinuousTimeMillis"); static final AtomicIntegerFieldUpdater failContinuousUpdater = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "failContinuous"); @@ -995,6 +1003,38 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.queryTimeout = seconds; } + /** + * + * @since 1.2.12 + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * + * @since 1.2.12 + */ + public void setConnectTimeout(int milliSeconds) { + this.connectTimeout = milliSeconds; + } + + /** + * + * @since 1.2.12 + */ + public int getSocketTimeout() { + return socketTimeout; + } + + /** + * + * @since 1.2.12 + */ + public void setSocketTimeout(int milliSeconds) { + this.socketTimeout = milliSeconds; + } + public String getName() { if (name != null) { return name; @@ -1691,6 +1731,17 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements physicalConnectProperties.put("password", password); } + if (connectTimeout > 0) { + if (isMySql) { + physicalConnectProperties.put("connectTimeout", connectTimeout); + } else if (isOracle) { + physicalConnectProperties.put("oracle.net.CONNECT_TIMEOUT", connectTimeout); + } else if (driver != null && "org.postgresql.Driver".equals(driver.getClass().getName())) { + physicalConnectProperties.put("loginTimeout", connectTimeout); + physicalConnectProperties.put("socketTimeout", connectTimeout); + } + } + Connection conn = null; long connectStartNanos = System.nanoTime(); @@ -1716,6 +1767,16 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements initPhysicalConnection(conn, variables, globalVariables); initedNanos = System.nanoTime(); + if (socketTimeout > 0 && !netTimeoutError) { + try { + conn.setNetworkTimeout(netTimeoutExecutor, socketTimeout); + } catch (SQLFeatureNotSupportedException | AbstractMethodError e) { + netTimeoutError = true; + } catch (Exception ignored) { + // ignored + } + } + validateConnection(conn); validatedNanos = System.nanoTime(); @@ -2225,4 +2286,19 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements return globalVairiables; } } + + class SynchronousExecutor implements Executor { + @Override + public void execute(Runnable command) { + try { + command.run(); + } catch (AbstractMethodError error) { + netTimeoutError = true; + } catch (Exception ignored) { + if (LOG.isDebugEnabled()) { + LOG.debug("failed to execute command " + command); + } + } + } + } } diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 4eb62baf21..ce54dad024 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -866,6 +866,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat initCheck(); + this.netTimeoutExecutor = new SynchronousExecutor(); + initExceptionSorter(); initValidConnectionChecker(); validationQueryCheck(); @@ -1221,7 +1223,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } else if (dbType == DbType.db2) { db2ValidationQueryCheck(); } else if (dbType == DbType.mysql - || JdbcUtils.MYSQL_DRIVER_6.equals(this.driverClass)) { + || JdbcUtils.MYSQL_DRIVER.equals(this.driverClass) + || JdbcUtils.MYSQL_DRIVER_6.equals(this.driverClass) + ) { isMySql = true; }