From 2b23a07734102cf5c789a68267c9e524d79b3e8d Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 22 Jan 2017 17:39:20 +0800 Subject: [PATCH] DruidDataSource add keepAlive support. --- .../druid/pool/DruidConnectionHolder.java | 21 ++++++ .../alibaba/druid/pool/DruidDataSource.java | 56 ++++++++++++--- .../http/resources/connectionInfo.html | 10 +-- .../support/http/resources/datasource.html | 40 +++++++++++ .../support/http/resources/js/lang.js | 8 +-- .../alibaba/druid/bvt/pool/KeepAliveTest.java | 9 +++ .../bvt/pool/KeepAliveTest_scheduler.java | 68 +++++++++++++++++++ 7 files changed, 194 insertions(+), 18 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest_scheduler.java diff --git a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java index a30ff1952c..10cf4f79a6 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java +++ b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java @@ -16,6 +16,7 @@ package com.alibaba.druid.pool; import com.alibaba.druid.pool.DruidAbstractDataSource.PhysicalConnectionInfo; +import com.alibaba.druid.proxy.jdbc.WrapperProxy; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.JdbcConstants; @@ -43,12 +44,14 @@ public final class DruidConnectionHolder { private final static Log LOG = LogFactory.getLog(DruidConnectionHolder.class); private final DruidAbstractDataSource dataSource; + private final long connectionId; private final Connection conn; private final List connectionEventListeners = new CopyOnWriteArrayList(); private final List statementEventListeners = new CopyOnWriteArrayList(); protected final long connectTimeMillis; protected transient long lastActiveTimeMillis; private long useCount = 0; + private long keepAliveCheckCount = 0; private long lastNotEmptyWaitNanos; @@ -103,6 +106,12 @@ public final class DruidConnectionHolder { this.underlyingAutoCommit = conn.getAutoCommit(); + if (conn instanceof WrapperProxy) { + this.connectionId = ((WrapperProxy) conn).getId(); + } else { + this.connectionId = dataSource.createConnectionId(); + } + { boolean initUnderlyHoldability = !holdabilityUnsupported; if (JdbcConstants.SYBASE.equals(dataSource.dbType) // @@ -240,10 +249,22 @@ public final class DruidConnectionHolder { return useCount; } + public long getConnectionId() { + return connectionId; + } + public void incrementUseCount() { useCount++; } + public long getKeepAliveCheckCount() { + return keepAliveCheckCount; + } + + public void incrementKeepAliveCheckCount() { + keepAliveCheckCount++; + } + public void reset() throws SQLException { // reset default settings if (underlyingReadOnly != defaultReadOnly) { diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 990d15c769..aa4e7f33c0 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -168,7 +168,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat private boolean logDifferentThread = true; - private boolean keepAlive = false; + private volatile boolean keepAlive = false; public DruidDataSource(){ this(false); @@ -325,6 +325,12 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat this.setKeepAlive(value); } } + { + Boolean value = getBoolean(properties, "druid.poolPreparedStatements"); + if (value != null) { + this.setPoolPreparedStatements0(value); + } + } } public boolean isUseGlobalDataSourceStat() { @@ -449,6 +455,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } public void setPoolPreparedStatements(boolean value) { + setPoolPreparedStatements0(value); + } + + private void setPoolPreparedStatements0(boolean value) { if (this.poolPreparedStatements == value) { return; } @@ -731,6 +741,19 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (connectError != null && poolingCount == 0) { throw connectError; } + + if (keepAlive) { + // async fill to minIdle + if (createScheduler != null) { + for (int i = 0; i < minIdle; ++i) { + createTaskCount++; + CreateConnectionTask task = new CreateConnectionTask(); + createScheduler.submit(task); + } + } else { + this.emptySignal(); + } + } } catch (SQLException e) { LOG.error("{dataSource-" + this.getID() + "} init error", e); throw e; @@ -1897,7 +1920,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (emptyWait) { // 必须存在线程等待,才创建连接 - if (poolingCount >= notEmptyWaitThreadCount) { + if (poolingCount >= notEmptyWaitThreadCount // + && !(keepAlive && activeCount + poolingCount < minIdle)) { createTaskCount--; return; } @@ -2012,7 +2036,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (emptyWait) { // 必须存在线程等待,才创建连接 - if (poolingCount >= notEmptyWaitThreadCount) { + if (poolingCount >= notEmptyWaitThreadCount // + && !(keepAlive && activeCount + poolingCount < minIdle)) { empty.await(); } @@ -2328,11 +2353,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (checkTime && i < checkCount) { evictConnections[evictCount++] = connection; } else if (idleMillis > maxEvictableIdleTimeMillis) { - if (keepAlive) { - keepAliveConnections[keepAliveCount++] = connection; - } else { - evictConnections[evictCount++] = connection; - } + evictConnections[evictCount++] = connection; + } else if (keepAlive) { + keepAliveConnections[keepAliveCount++] = connection; } } else { if (i < checkCount) { @@ -2366,9 +2389,11 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (keepAliveCount > 0) { this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount); - for (int i = 0; i < keepAliveCount; ++i) { + // keep order + for (int i = keepAliveCount - 1; i >= 0; --i) { DruidConnectionHolder holer = keepAliveConnections[i]; Connection connection = holer.getConnection(); + holer.incrementKeepAliveCheckCount(); boolean validate = false; try { @@ -2560,6 +2585,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat Map map = new LinkedHashMap(); map.put("id", System.identityHashCode(conn)); + map.put("connectionId", connHolder.getConnectionId()); map.put("useCount", connHolder.getUseCount()); if (connHolder.getLastActiveTimeMillis() > 0) { map.put("lastActiveTime", new Date(connHolder.getLastActiveTimeMillis())); @@ -2577,7 +2603,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat Map stmtInfo = new LinkedHashMap(); stmtInfo.put("sql", stmtHolder.key.getSql()); - stmtInfo.put("defaultRowPretch", stmtHolder.getDefaultRowPrefetch()); + stmtInfo.put("defaultRowPrefetch", stmtHolder.getDefaultRowPrefetch()); stmtInfo.put("rowPrefetch", stmtHolder.getRowPrefetch()); stmtInfo.put("hitCount", stmtHolder.getHitCount()); @@ -2586,6 +2612,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat map.put("pscache", stmtCache); } + map.put("keepAliveCheckCount", connHolder.getKeepAliveCheckCount()); list.add(map); } @@ -2799,6 +2826,15 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat dataMap.put("BlobOpenCount", this.getDataSourceStat().getBlobOpenCount()); dataMap.put("KeepAliveCheckCount", this.getDataSourceStat().getKeepAliveCheckCount()); + dataMap.put("KeepAlive", this.isKeepAlive()); + dataMap.put("FailFast", this.isFailFast()); + dataMap.put("MaxWait", this.getMaxWait()); + 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()); + return dataMap; } diff --git a/src/main/resources/support/http/resources/connectionInfo.html b/src/main/resources/support/http/resources/connectionInfo.html index c3026db979..7db29f2f81 100644 --- a/src/main/resources/support/http/resources/connectionInfo.html +++ b/src/main/resources/support/http/resources/connectionInfo.html @@ -20,19 +20,20 @@ ID - UseCount + UseCnt + KACheckCnt LastActiveTime ConnectTime Holdability TranIsolation AutoCommit - ReadoOnly + ReadOnly Pscache
- + @@ -84,8 +85,9 @@ var stat = statList[i]; var newRow = sqlStatTable.insertRow(-1); html += ""; - html += ""; + html += ""; html += ""; + html += ""; if (stat.lastActiveTime) html += ""; diff --git a/src/main/resources/support/http/resources/datasource.html b/src/main/resources/support/http/resources/datasource.html index 320d4dc6dc..c04cd80715 100644 --- a/src/main/resources/support/http/resources/datasource.html +++ b/src/main/resources/support/http/resources/datasource.html @@ -133,6 +133,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/support/http/resources/js/lang.js b/src/main/resources/support/http/resources/js/lang.js index 16bae8f87a..72c145200f 100644 --- a/src/main/resources/support/http/resources/js/lang.js +++ b/src/main/resources/support/http/resources/js/lang.js @@ -69,7 +69,7 @@ druid.lang = function() { 'RollbackCount' : ['RollbackCount', '回滚数'], 'PSCacheAccessCount' : ['PSCacheAccessCount', 'PSCache访问次数'], 'PSCacheHitCount' : ['PSCacheHitCount', 'PSCache命中次数'], - 'PSCacheMissCount' : ['PSCacheMissCount', 'PSCache丢失次数'],// + 'PSCacheMissCount' : ['PSCacheMissCount', 'PSCache不命中次数'],// 'ConnectionHoldTimeHistogram' : ['ConnectionHoldTimeHistogram', '连接持有时间分布'], 'ClobOpenCount' : ['ClobOpenCount', 'Clob打开次数'], 'BlobOpenCount' : ['BlobOpenCount', 'Blob打开次数'], @@ -116,11 +116,11 @@ druid.lang = function() { 'PhysicalConnectErrorCountDesc' : ['Total physical connect error times', '产生的物理连接失败总数'], 'ExecuteCountDesc' : ['', ''], 'ErrorCountDesc' : ['', ''], - 'CommitCountDesc' : ['', ''], - 'RollbackCountDesc' : ['', ''], + 'CommitCountDesc' : ['', '事务提交次数'], + 'RollbackCountDesc' : ['', '事务回滚次数'], 'PSCacheAccessCountDesc' : ['PerpareStatement access count', 'PSCache访问总数'], 'PSCacheHitCountDesc' : ['PerpareStatement hit count', 'PSCache命中次数'], - 'PSCacheMissCountDesc' : ['PerpareStatement miss count', 'PSCache丢失次数'],// + 'PSCacheMissCountDesc' : ['PerpareStatement miss count', 'PSCache不命中次数'],// 'ConnectionHoldTimeHistogramDesc' : ['The histogram values of connection hold time, [0-1 ms, 1-10 ms, 10-100 ms, 100ms-1s, 1-10 s, 10-100 s, 100-1000 s, >1000 s]', '连接持有时间分布,分布区间为[0-1 ms, 1-10 ms, 10-100 ms, 100ms-1s, 1-10 s, 10-100 s, 100-1000 s, >1000 s]'], 'ClobOpenCountDesc' : ['', 'Clob打开数'], 'BlobOpenCountDesc' : ['', 'Blob打开数'], diff --git a/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest.java b/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest.java index 74a39ff8b7..098bf04bb2 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest.java @@ -35,6 +35,15 @@ public class KeepAliveTest extends TestCase { } public void test_keepAlive() throws Exception { + dataSource.init(); + + for (int i = 0; i < 1000; ++i) { + if (dataSource.getMinIdle() == dataSource.getPoolingCount()) { + break; + } + Thread.sleep(10 * 1); + } + assertEquals(dataSource.getMinIdle(), dataSource.getPoolingCount()); assertTrue(dataSource.isKeepAlive()); Connection[] connections = new Connection[dataSource.getMaxActive()]; diff --git a/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest_scheduler.java b/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest_scheduler.java new file mode 100644 index 0000000000..5b646da5c3 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/KeepAliveTest_scheduler.java @@ -0,0 +1,68 @@ +package com.alibaba.druid.bvt.pool; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; +import junit.framework.TestCase; + +import java.sql.Connection; +import java.util.Properties; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * Created by wenshao on 21/01/2017. + */ +public class KeepAliveTest_scheduler extends TestCase { + private DruidDataSource dataSource; + private ScheduledExecutorService scheduler; + + protected void setUp() throws Exception { + scheduler = Executors.newScheduledThreadPool(10); + + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:x1"); + dataSource.setPoolPreparedStatements(true); + dataSource.setMinIdle(10); + dataSource.setMaxActive(20); +// dataSource.setMinEvictableIdleTimeMillis(30000); +// dataSource.setMaxEvictableIdleTimeMillis(30000); + dataSource.setTimeBetweenEvictionRunsMillis(10); + dataSource.setFilters("log4j"); + dataSource.setValidationQuery("select 1"); + + Properties properties = new Properties(); + properties.put("druid.keepAlive", "true"); + dataSource.configFromPropety(properties); + dataSource.setCreateScheduler(scheduler); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + } + + public void test_keepAlive() throws Exception { + dataSource.init(); + + for (int i = 0; i < 1000; ++i) { + if (dataSource.getMinIdle() == dataSource.getPoolingCount()) { + break; + } + Thread.sleep(10 * 1); + } + assertEquals(dataSource.getMinIdle(), dataSource.getPoolingCount()); + assertTrue(dataSource.isKeepAlive()); + + Connection[] connections = new Connection[dataSource.getMaxActive()]; + for (int i = 0; i < connections.length; ++i) { + connections[i] = dataSource.getConnection(); + } + for (int i = 0; i < connections.length; ++i) { + connections[i].close(); + } + // assertEquals(dataSource.getMaxActive(), dataSource.getPoolingCount()); + + + + // Thread.sleep(1000 * 1000); + } +}
sqldefaultRowPrefetchdfRowPrefetch rowPrefetch hitCount
" + stat.id + "" + stat.connectionId + "" + stat.useCount + "" + stat.keepAliveCheckCount + "" + stat.lastActiveTime + "{{=datasourceNow.DefaultTransactionIsolation}}
* MinEvictableIdleTimeMillis{{=datasourceNow.MinEvictableIdleTimeMillis}}
* MaxEvictableIdleTimeMillis{{=datasourceNow.MaxEvictableIdleTimeMillis}}
* KeepAlive{{=datasourceNow.KeepAlive}}
* FailFast{{=datasourceNow.FailFast}}
* PoolPreparedStatements{{=datasourceNow.PoolPreparedStatements}}
* MaxPoolPreparedStatementPerConnectionSize{{=datasourceNow.MaxPoolPreparedStatementPerConnectionSize}}
* MaxWait{{=datasourceNow.MaxWait}}
* MaxWaitThreadCount{{=datasourceNow.MaxWaitThreadCount}}
NotEmptyWaitCount {{=datasourceNow.NotEmptyWaitCount}}