From 3b2acf03bc6a97d26b3e2e5455e462123c70f7a5 Mon Sep 17 00:00:00 2001 From: wenshao Date: Tue, 13 Jun 2017 19:04:49 +0800 Subject: [PATCH] DruidDataSource support killWhenSocketReadTimeout --- .../druid/pool/DruidConnectionHolder.java | 2 +- .../alibaba/druid/pool/DruidDataSource.java | 17 +++- .../druid/pool/DruidPooledStatement.java | 77 +++++++++++++++++++ .../com/alibaba/druid/util/MySqlUtils.java | 17 ++++ .../druid/pool/ExceptionSorterKillTest.java | 7 ++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/pool/ExceptionSorterKillTest.java diff --git a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java index 32b7ebf739..2e1da9731f 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java +++ b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java @@ -44,7 +44,7 @@ public final class DruidConnectionHolder { private final static Log LOG = LogFactory.getLog(DruidConnectionHolder.class); - private final DruidAbstractDataSource dataSource; + protected final DruidAbstractDataSource dataSource; private final long connectionId; private final Connection conn; private final List connectionEventListeners = new CopyOnWriteArrayList(); diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index fcfb40deca..44267f5fed 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -37,7 +37,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -173,6 +172,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat private volatile boolean keepAlive = false; + protected boolean killWhenSocketReadTimeout = false; + public DruidDataSource(){ this(false); } @@ -387,6 +388,20 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } } } + { + Boolean value = getBoolean(properties, "druid.killWhenSocketReadTimeout"); + if (value != null) { + setKillWhenSocketReadTimeout(value); + } + } + } + + public boolean isKillWhenSocketReadTimeout() { + return killWhenSocketReadTimeout; + } + + public void setKillWhenSocketReadTimeout(boolean killWhenSocketTimeOut) { + this.killWhenSocketReadTimeout = killWhenSocketTimeOut; } public boolean isUseGlobalDataSourceStat() { diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java index e60a2da6db..64ac35805a 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -17,7 +17,11 @@ package com.alibaba.druid.pool; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcConstants; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.MySqlUtils; +import java.net.SocketTimeoutException; import java.sql.*; import java.util.ArrayList; import java.util.List; @@ -61,10 +65,83 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { } protected SQLException checkException(Throwable error) throws SQLException { + handleScoketTimeout(error); + exceptionCount++; return conn.handleException(error); } + private void handleScoketTimeout(Throwable error) throws SQLException { + if (this.conn == null + || this.conn.transactionInfo != null + || this.conn.holder == null) { + return; + } + + DruidDataSource dataSource = null; + + if (this.conn.holder.dataSource instanceof DruidDataSource) { + dataSource = (DruidDataSource) this.conn.holder.dataSource; + } + if (dataSource == null) { + return; + } + + if (!dataSource.killWhenSocketReadTimeout) { + return; + } + + SQLException sqlException = null; + if (error instanceof SQLException) { + sqlException = (SQLException) error; + } + + if (sqlException == null) { + return; + } + + + Throwable cause = error.getCause(); + boolean socketReadTimeout = cause instanceof SocketTimeoutException + && "Read timed out".equals(cause.getMessage()); + if (!socketReadTimeout) { + return; + } + + if (!JdbcConstants.MYSQL.equals(dataSource.dbType)) { + return; + } + + String killQuery = MySqlUtils.buildKillQuerySql(this.conn.getConnection(), (SQLException) error); + + if (killQuery == null) { + return; + } + + DruidPooledConnection killQueryConn = null; + Statement killQueryStmt = null; + + + try { + killQueryConn = dataSource.getConnection(1000); + if (killQueryConn == null) { + return; + } + + killQueryStmt = killQueryConn.createStatement(); + killQueryStmt.execute(killQuery); + + if (LOG.isDebugEnabled()) { + LOG.debug(killQuery + " success."); + } + } catch (Exception ex) { + LOG.warn(killQuery + " error.", ex); + } finally { + JdbcUtils.close(killQueryStmt); + JdbcUtils.close(killQueryConn); + } + } + public DruidPooledConnection getPoolableConnection() { return conn; } diff --git a/src/main/java/com/alibaba/druid/util/MySqlUtils.java b/src/main/java/com/alibaba/druid/util/MySqlUtils.java index fbf05c52fb..f4220b6025 100644 --- a/src/main/java/com/alibaba/druid/util/MySqlUtils.java +++ b/src/main/java/com/alibaba/druid/util/MySqlUtils.java @@ -15,8 +15,14 @@ */ package com.alibaba.druid.util; +import com.alibaba.druid.sql.ast.SQLStatement; +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.Connection; @@ -87,4 +93,15 @@ public class MySqlUtils { throw new SQLFeatureNotSupportedException(); } + + public static String buildKillQuerySql(Connection connection, SQLException error) throws SQLException { + try { + ConnectionImpl connImpl = (ConnectionImpl) connection; + long threadId = connImpl.getId(); + return "KILL QUERY " + threadId; + } catch (Exception e) { + // skip + } + return null; + } } diff --git a/src/test/java/com/alibaba/druid/pool/ExceptionSorterKillTest.java b/src/test/java/com/alibaba/druid/pool/ExceptionSorterKillTest.java new file mode 100644 index 0000000000..ce6ad5d689 --- /dev/null +++ b/src/test/java/com/alibaba/druid/pool/ExceptionSorterKillTest.java @@ -0,0 +1,7 @@ +package com.alibaba.druid.pool; + +/** + * Created by wenshao on 13/06/2017. + */ +public class ExceptionSorterKillTest { +}