diff --git a/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java b/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java index d39c7a7cff..be6d123ce6 100644 --- a/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java +++ b/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java @@ -28,6 +28,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.parser.SQLStatementParser; @@ -48,6 +49,10 @@ public class MySqlMockExecuteHandlerImpl implements MockExecuteHandler { SQLStatement stmt = stmtList.get(0); + if (stmt instanceof CobarShowStatus) { + return showStatus(statement); + } + if (!(stmt instanceof SQLSelectStatement)) { throw new SQLException("executeQueryError : " + sql); } @@ -80,7 +85,21 @@ public class MySqlMockExecuteHandlerImpl implements MockExecuteHandler { } else { throw new SQLException("TODO"); } + } + public ResultSet showStatus(MockStatement statement) throws SQLException { + MockResultSet rs = new MockResultSet(statement); + MockResultSetMetaData metaData = rs.getMockMetaData(); + + Object[] row = new Object[] { "on" }; + + ColumnMetaData column = new ColumnMetaData(); + column.setColumnType(Types.NVARCHAR); + metaData.getColumns().add(column); + + rs.getRows().add(row); + + return rs; } public ResultSet executeQueryFromDual(MockStatement statement, SQLSelectQueryBlock query) throws SQLException { diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 1905150412..80097c21fe 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -197,6 +197,12 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements private ObjectName objectName; + protected Exception createError = null; + + public Exception getCreateError() { + return createError; + } + public boolean isDupCloseLogEnable() { return dupCloseLogEnable; } @@ -1137,11 +1143,14 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } dataSource.validateConnection(conn); + dataSource.createError = null; } catch (SQLException ex) { dataSource.createErrorCount++; + dataSource.createError = ex; throw ex; } catch (RuntimeException ex) { dataSource.createErrorCount++; + dataSource.createError = ex; throw ex; } catch (Error ex) { dataSource.createErrorCount++; diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index e94c982189..c4ad663c51 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -735,7 +735,11 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat continue; } - throw new GetConnectionTimeoutException(); + if (createError != null) { + throw new GetConnectionTimeoutException(createError); + } else { + throw new GetConnectionTimeoutException(); + } } } diff --git a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java index 2425ab9906..a751474041 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java @@ -61,6 +61,7 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc private boolean inited = false; protected final Lock lock = new ReentrantLock(); protected final Condition notFull = lock.newCondition(); + protected final Condition notFail = lock.newCondition(); private ConcurrentMap dataSources = new ConcurrentHashMap(); @@ -81,6 +82,23 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc private int maxPoolSize = 50; private long activeCount = 0; + private long maxWaitMillis = 0; + + public long getMaxWaitMillis() { + return maxWaitMillis; + } + + public void setMaxWaitMillis(long maxWaitMillis) { + this.maxWaitMillis = maxWaitMillis; + } + + public void setMaxWait(long seconds) { + this.setMaxWaitMillis(1000 * seconds); + } + + public long getMaxWait() { + return this.getMaxWaitMillis() / 1000; + } public int getMaxPoolSize() { return maxPoolSize; @@ -162,6 +180,7 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc scheduler = Executors.newScheduledThreadPool(schedulerThreadCount); startFailureDetectScheduleTask(); + startConfigLoadScheduleTask(); inited = true; MultiDataSourceStatManager.add(this); @@ -171,7 +190,7 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc } public boolean startConfigLoadScheduleTask() { - if (configLoadFuture != null) { + if (configLoadFuture == null) { configLoadFuture = scheduler.scheduleAtFixedRate(new ConfigLoadTask(), configLoadPeriodMillis, configLoadPeriodMillis, TimeUnit.MILLISECONDS); return true; @@ -309,6 +328,13 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc holder.setWeightRegionEnd(totalWeight); } this.totalWeight = totalWeight; + + lock.lock(); + try { + notFail.signalAll(); + } finally { + lock.unlock(); + } } public Connection getConnection() throws SQLException { @@ -341,13 +367,21 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc lock.unlock(); } } - + public int produceRandomNumber() { + if (totalWeight == 0) { + return 0; + } + return random.nextInt(totalWeight); } - public MultiConnectionHolder getConnectionInternal(MultiDataSourceConnection multiConn, String sql) - throws SQLException { + public MultiConnectionHolder getRealConnection(MultiDataSourceConnection multiConn, String sql) throws SQLException { + + long startNano = -1; + if (maxWaitMillis > 0) { + startNano = System.nanoTime(); + } DataSourceHolder dataSource = null; @@ -385,12 +419,52 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc if (dataSource == null) { dataSource = first; } + + if (dataSource == null && i != MAX_RETRY - 1) { + lock.lock(); + try { + if (getEnabledDataSourceCount() == 0) { + try { + if (maxWaitMillis > 0) { + long nano = System.nanoTime() - startNano; + long restNano = maxWaitMillis * 1000 * 1000 - nano; + if (restNano > 0) { + notFail.awaitNanos(restNano); + } else { + break; + } + } else { + notFail.await(); + } + continue; + } catch (InterruptedException e) { + throw new SQLException("interrupted", e); + } + } + } finally { + lock.unlock(); + } + } break; } + if (dataSource == null) { + throw new SQLException("cannot get connection. enabledDataSourceCount " + getEnabledDataSourceCount()); + } + return dataSource.getConnection(); } + public int getEnabledDataSourceCount() { + int count = 0; + for (DataSourceHolder item : this.dataSources.values()) { + if (item.isEnable()) { + count++; + } + } + return count; + } + public void handleNotAwailableDatasource(DataSourceHolder dataSourceHolder) { } diff --git a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSourceConnection.java b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSourceConnection.java index 096b3524df..a87f778567 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSourceConnection.java +++ b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSourceConnection.java @@ -62,7 +62,7 @@ public class MultiDataSourceConnection extends WrapperAdapter implements Connect public void checkConnection(String sql) throws SQLException { if (conn == null) { - MultiConnectionHolder connHolder = haDataSource.getConnectionInternal(this, sql); + MultiConnectionHolder connHolder = haDataSource.getRealConnection(this, sql); conn = connHolder.getConnection(); this.dataSourceHolder = connHolder.getDataSourceHolder(); } diff --git a/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarConfigLoader.java b/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarConfigLoader.java index 2c013cde4b..b903706cd3 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarConfigLoader.java +++ b/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarConfigLoader.java @@ -48,8 +48,8 @@ public class CobarConfigLoader extends URLConnectionConfigLoader implements Conf { int pos = ip.indexOf(':'); if (pos != -1) { - ip = ip.substring(0, pos); port = Integer.parseInt(ip.substring(pos + 1)); + ip = ip.substring(0, pos); } } @@ -64,8 +64,10 @@ public class CobarConfigLoader extends URLConnectionConfigLoader implements Conf } @Override - protected void handleResponseMessage(String response) throws SQLException { - JSONObject json = JSON.parseObject(response); + protected void handleResponseMessage() throws SQLException { + String responseMessage = this.getResponseMessage(); + + JSONObject json = JSON.parseObject(responseMessage); String errorMessage = json.getString("error"); if (errorMessage != null) { throw new SQLException("load config error, message : " + errorMessage); @@ -88,7 +90,7 @@ public class CobarConfigLoader extends URLConnectionConfigLoader implements Conf DataSourceHolder holder = dataSource.getDataSourceHolder(key); if (holder == null) { - String jdbcUrl = "jdbc:mysql://" + ip + ":" + port + "/" + schema; + String jdbcUrl = createJdbcUrl(ip, port, schema); holder = dataSource.createDataSourceHolder(jdbcUrl, weight); dataSource.addDataSource(key, holder); } else { @@ -114,4 +116,8 @@ public class CobarConfigLoader extends URLConnectionConfigLoader implements Conf } } + protected String createJdbcUrl(String ip, int port, String schema) { + return "jdbc:mysql://" + ip + ":" + port + "/" + schema; + } + } diff --git a/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarDataSource.java b/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarDataSource.java index ec2a6b0632..e73d856a48 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/ha/cobar/CobarDataSource.java @@ -35,6 +35,7 @@ public class CobarDataSource extends MultiDataSource { private List proxyFilters = new ArrayList(); private String filters; + public CobarDataSource(){ this.setFailureDetector(new CobarFailureDetecter()); this.setFailureDetectPeriodMillis(DEFAULT_FAILURE_DETECT_PERRIOD_MILLIS); @@ -103,8 +104,10 @@ public class CobarDataSource extends MultiDataSource { } if (CobarConfigLoader.isCobar(url)) { - this.setConfigLoader(new CobarConfigLoader(this)); - + if (this.getConfigLoader() == null) { + this.setConfigLoader(new CobarConfigLoader(this)); + } + final int RETRY_COUNT = 3; for (int i = 0; i < RETRY_COUNT; ++i) { try { diff --git a/src/main/java/com/alibaba/druid/pool/ha/config/URLConnectionConfigLoader.java b/src/main/java/com/alibaba/druid/pool/ha/config/URLConnectionConfigLoader.java index fabf69d0cd..888d91b17e 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/config/URLConnectionConfigLoader.java +++ b/src/main/java/com/alibaba/druid/pool/ha/config/URLConnectionConfigLoader.java @@ -11,9 +11,11 @@ import com.alibaba.druid.util.JdbcUtils; public abstract class URLConnectionConfigLoader implements ConfigLoader { - private URL url; - private int connectTimeout = 1000 * 3; - private int readTimeout; + private URL url; + private int connectTimeout = 1000 * 3; + private int readTimeout; + + protected String responseMessage; public URLConnectionConfigLoader(URL configServerURL){ this.url = configServerURL; @@ -47,14 +49,16 @@ public abstract class URLConnectionConfigLoader implements ConfigLoader { this.readTimeout = readTimeout; } + public String getResponseMessage() { + return responseMessage; + } + @Override public void load() throws SQLException { if (url == null) { throw new IllegalStateException("configServerURL is null"); } - String responseMessage; - { HttpURLConnection conn = null; Reader reader = null; @@ -68,17 +72,17 @@ public abstract class URLConnectionConfigLoader implements ConfigLoader { reader = new InputStreamReader(conn.getInputStream()); responseMessage = JdbcUtils.read(reader); - - handleResponseMessage(responseMessage); + + handleResponseMessage(); } catch (Exception e) { - throw new SQLException("load config error, url : " + url.toString()); + throw new SQLException("load config error, url : " + url.toString(), e); } finally { JdbcUtils.close(reader); conn.disconnect(); } } } - - protected abstract void handleResponseMessage(String response) throws SQLException; + + protected abstract void handleResponseMessage() throws SQLException; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/CobarShowStatus.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/CobarShowStatus.java new file mode 100644 index 0000000000..ec23338a28 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/CobarShowStatus.java @@ -0,0 +1,14 @@ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class CobarShowStatus extends MySqlStatementImpl { + + private static final long serialVersionUID = 1L; + + protected void accept0(MySqlASTVisitor visitor) { + visitor.visit(this); + + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index c5cc16a371..86feb9e6da 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -29,6 +29,7 @@ import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableParser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; @@ -292,6 +293,11 @@ public class MySqlStatementParser extends SQLStatementParser { return stmt; } } + + if (identifierEquals("COBAR_STATUS")) { + lexer.nextToken(); + return new CobarShowStatus(); + } throw new ParserException("TODO"); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index 58f0b6c88d..51bc25e734 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; @@ -143,4 +144,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { void endVisit(MySqlShowStatusStatement x); boolean visit(MySqlShowStatusStatement x); + + void endVisit(CobarShowStatus x); + + boolean visit(CobarShowStatus x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java index 6c774fd09d..9900046561 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java @@ -7,6 +7,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; @@ -300,4 +301,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq return true; } + @Override + public void endVisit(CobarShowStatus x) { + + } + + @Override + public boolean visit(CobarShowStatus x) { + return true; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 83c26f0da1..9d4df07315 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -36,6 +36,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; @@ -1190,4 +1191,14 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV return false; } + @Override + public void endVisit(CobarShowStatus x) { + + } + + @Override + public boolean visit(CobarShowStatus x) { + print("SHOW COBAR_STATUS"); + return false; + } } diff --git a/src/main/java/com/alibaba/druid/util/JdbcUtils.java b/src/main/java/com/alibaba/druid/util/JdbcUtils.java index 8106965653..6e2d480f9a 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcUtils.java @@ -501,6 +501,9 @@ public final class JdbcUtils { } public static String toString(java.util.Date date) { + if (date == null) { + return null; + } SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(date); } diff --git a/src/test/java/com/alibaba/druid/DruidTest.java b/src/test/java/com/alibaba/druid/DruidTest.java new file mode 100644 index 0000000000..cf907d7bc5 --- /dev/null +++ b/src/test/java/com/alibaba/druid/DruidTest.java @@ -0,0 +1,21 @@ +package com.alibaba.druid; + +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; + +public class DruidTest extends TestCase { + + public void test_0() throws Exception { + DruidDataSource ds = new DruidDataSource(); + ds.setMaxWait(30); + ds.setInitialSize(0); + ds.setUrl("jdbc:mysql:xxx"); + + try { + ds.getConnection(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/ha/CobarDataSourceTest.java b/src/test/java/com/alibaba/druid/bvt/pool/ha/CobarDataSourceTest.java new file mode 100644 index 0000000000..5bc4b46eb3 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/ha/CobarDataSourceTest.java @@ -0,0 +1,118 @@ +package com.alibaba.druid.bvt.pool.ha; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.mock.MockConnection; +import com.alibaba.druid.pool.ha.cobar.CobarConfigLoader; +import com.alibaba.druid.pool.ha.cobar.CobarDataSource; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +public class CobarDataSourceTest extends TestCase { + + public void test_cobarDataSource() throws Exception { + String url = "jdbc:cobar://127.0.0.1:8080/druid"; + + final JSONObject config = new JSONObject(); + + JSONArray cobarList = new JSONArray(); + + JSONObject cobarA = new JSONObject(); + cobarA.put("ip", "mock"); + cobarA.put("port", 80); + cobarA.put("schema", "cobarA"); + cobarA.put("weight", 1); + + cobarList.add(cobarA); + + JSONObject cobarB = new JSONObject(); + cobarB.put("ip", "mock"); + cobarB.put("port", 81); + cobarB.put("schema", "cobarB"); + cobarB.put("weight", 1); + + cobarList.add(cobarB); + + String mockUrlA = "jdbc:mock://mock:80/cobarA"; + String mockUrlB = "jdbc:mock://mock:81/cobarB"; + + config.put("cobarList", cobarList); + + final CobarDataSource dataSource = new CobarDataSource(); + dataSource.setUrl(url); + dataSource.setUsername("test"); + dataSource.setPassword(""); + dataSource.setMaxWait(1); + dataSource.setConfigLoadPeriodMillis(3); + + CobarConfigLoader configLoader = new CobarConfigLoader(dataSource) { + + public void load() throws SQLException { + responseMessage = config.toJSONString(); + + handleResponseMessage(); + } + + protected String createJdbcUrl(String ip, int port, String schema) { + return "jdbc:mock://" + ip + ":" + port + "/" + schema; + } + }; + dataSource.setConfigLoader(configLoader); + + { + Connection conn = dataSource.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("select 1"); + rs.next(); + rs.close(); + stmt.close(); + conn.close(); + } + + cobarList.remove(cobarB); + Assert.assertEquals(1, cobarList.size()); + Thread.sleep(dataSource.getConfigLoadPeriodMillis() * 2); + for (int i = 0; i < 100; ++i) { + Connection conn = dataSource.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("select 1"); + + MockConnection mockConn = conn.unwrap(MockConnection.class); + Assert.assertNotNull(mockConn); + Assert.assertEquals(mockUrlA, mockConn.getUrl()); + + rs.next(); + rs.close(); + stmt.close(); + conn.close(); + } + + cobarList.add(cobarB); + Assert.assertEquals(2, cobarList.size()); + cobarList.remove(cobarA); + Assert.assertEquals(1, cobarList.size()); + Thread.sleep(dataSource.getConfigLoadPeriodMillis() * 2); + for (int i = 0; i < 100; ++i) { + Connection conn = dataSource.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("select 1"); + + MockConnection mockConn = conn.unwrap(MockConnection.class); + Assert.assertNotNull(mockConn); + Assert.assertEquals(mockUrlB, mockConn.getUrl()); + + rs.next(); + rs.close(); + stmt.close(); + conn.close(); + } + } + +} diff --git a/src/test/java/com/alibaba/druid/pool/ha/CobarDataSourceTest.java b/src/test/java/com/alibaba/druid/pool/ha/CobarDataSourceTest.java new file mode 100644 index 0000000000..c16d712482 --- /dev/null +++ b/src/test/java/com/alibaba/druid/pool/ha/CobarDataSourceTest.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.pool.ha; + +import java.sql.Connection; +import java.sql.Statement; +import java.util.concurrent.CountDownLatch; + +import junit.framework.TestCase; + +import com.alibaba.druid.pool.ha.cobar.CobarDataSource; + +public class CobarDataSourceTest extends TestCase { + + public void test_cobar() throws Exception { + String url = "jdbc:cobar://10.20.165.79:8080/druid"; + + final CobarDataSource dataSource = new CobarDataSource(); + dataSource.setUrl(url); + dataSource.setUsername("test"); + dataSource.setPassword(""); + + final int COUNT = 10; + final CountDownLatch latch = new CountDownLatch(COUNT); + + Runnable task = new Runnable() { + + public void run() { + try { + for (int i = 0; i < 1000 * 1000; ++i) { + Connection conn = dataSource.getConnection(); + + Statement stmt = conn.createStatement(); + stmt.execute("select 1"); + stmt.close(); + + conn.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + latch.countDown(); + } + } + }; + + + for (int i = 0; i < 10; ++i) { + Thread thread = new Thread(task); + thread.setName("thread-" + i); + thread.start(); + } + + latch.await(); + } +}