mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
Merge pull request #2485 from digitalsonic/master
为Druid增加URL占位符,使得Druid的一个DataSource可以同时连接多个目标
This commit is contained in:
@@ -62,6 +62,8 @@ public interface FilterChain {
|
||||
|
||||
int getFilterSize();
|
||||
|
||||
int getPos();
|
||||
|
||||
FilterChain cloneChain();
|
||||
|
||||
<T> T unwrap(Wrapper wrapper, java.lang.Class<T> iface) throws java.sql.SQLException;
|
||||
|
||||
@@ -92,6 +92,10 @@ public class FilterChainImpl implements FilterChain {
|
||||
return filterSize;
|
||||
}
|
||||
|
||||
public int getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
@@ -111,12 +111,12 @@ public class ConfigFilter extends FilterAdapter {
|
||||
}
|
||||
|
||||
DruidDataSource dataSource = (DruidDataSource) dataSourceProxy;
|
||||
Properties connectinProperties = dataSource.getConnectProperties();
|
||||
Properties connectionProperties = dataSource.getConnectProperties();
|
||||
|
||||
Properties configFileProperties = loadPropertyFromConfigFile(connectinProperties);
|
||||
Properties configFileProperties = loadPropertyFromConfigFile(connectionProperties);
|
||||
|
||||
// 判断是否需要解密,如果需要就进行解密行动
|
||||
boolean decrypt = isDecrypt(connectinProperties, configFileProperties);
|
||||
boolean decrypt = isDecrypt(connectionProperties, configFileProperties);
|
||||
|
||||
if (configFileProperties == null) {
|
||||
if (decrypt) {
|
||||
@@ -136,8 +136,8 @@ public class ConfigFilter extends FilterAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDecrypt(Properties connectinProperties, Properties configFileProperties) {
|
||||
String decrypterId = connectinProperties.getProperty(CONFIG_DECRYPT);
|
||||
public boolean isDecrypt(Properties connectionProperties, Properties configFileProperties) {
|
||||
String decrypterId = connectionProperties.getProperty(CONFIG_DECRYPT);
|
||||
if (decrypterId == null || decrypterId.length() == 0) {
|
||||
if (configFileProperties != null) {
|
||||
decrypterId = configFileProperties.getProperty(CONFIG_DECRYPT);
|
||||
@@ -151,8 +151,8 @@ public class ConfigFilter extends FilterAdapter {
|
||||
return Boolean.valueOf(decrypterId);
|
||||
}
|
||||
|
||||
Properties loadPropertyFromConfigFile(Properties connectinProperties) {
|
||||
String configFile = connectinProperties.getProperty(CONFIG_FILE);
|
||||
Properties loadPropertyFromConfigFile(Properties connectionProperties) {
|
||||
String configFile = connectionProperties.getProperty(CONFIG_FILE);
|
||||
|
||||
if (configFile == null) {
|
||||
configFile = System.getProperty(SYS_PROP_CONFIG_FILE);
|
||||
@@ -206,14 +206,14 @@ public class ConfigFilter extends FilterAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey(Properties connectinProperties, Properties configFileProperties) {
|
||||
public PublicKey getPublicKey(Properties connectionProperties, Properties configFileProperties) {
|
||||
String key = null;
|
||||
if (configFileProperties != null) {
|
||||
key = configFileProperties.getProperty(CONFIG_KEY);
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(key) && connectinProperties != null) {
|
||||
key = connectinProperties.getProperty(CONFIG_KEY);
|
||||
if (StringUtils.isEmpty(key) && connectionProperties != null) {
|
||||
key = connectionProperties.getProperty(CONFIG_KEY);
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.alibaba.druid.pool.ha;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* An utility class to create DruidDataSource dynamically.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class DataSourceCreator {
|
||||
private final static Log LOG = LogFactory.getLog(DataSourceCreator.class);
|
||||
|
||||
private Properties properties = new Properties();
|
||||
private List<String> nameList = new ArrayList<String>();
|
||||
|
||||
public DataSourceCreator(String file) {
|
||||
loadProperties(file);
|
||||
loadNameList();
|
||||
}
|
||||
|
||||
public Map<String, DataSource> createMap(HighAvailableDataSource haDataSource) throws SQLException {
|
||||
Map<String, DataSource> map = new ConcurrentHashMap<String, DataSource>();
|
||||
|
||||
if (nameList == null || nameList.isEmpty()) {
|
||||
LOG.error("No DataSource will be created!");
|
||||
return map;
|
||||
}
|
||||
|
||||
for (String n : nameList) {
|
||||
String url = properties.getProperty(n + ".url");
|
||||
String username = properties.getProperty(n + ".username");
|
||||
String password = properties.getProperty(n + ".password");
|
||||
LOG.info("Creating " + n + " with url[" + url + "] and username[" + username + "].");
|
||||
DruidDataSource dataSource = create(n, url, username, password, haDataSource);
|
||||
map.put(n, dataSource);
|
||||
}
|
||||
LOG.info(map.size() + " DruidDataSource(s) created. ");
|
||||
return map;
|
||||
}
|
||||
|
||||
protected DruidDataSource create(String name, String url, String username, String password,
|
||||
HighAvailableDataSource haDataSource) throws SQLException {
|
||||
DruidDataSource dataSource = new DruidDataSource();
|
||||
|
||||
dataSource.setName(name);
|
||||
dataSource.setUrl(url);
|
||||
dataSource.setUsername(username);
|
||||
dataSource.setPassword(password);
|
||||
|
||||
dataSource.setDriverClassName(haDataSource.getDriverClassName());
|
||||
dataSource.setConnectProperties(haDataSource.getConnectProperties());
|
||||
dataSource.setConnectionProperties(haDataSource.getConnectionProperties());
|
||||
|
||||
dataSource.setInitialSize(haDataSource.getInitialSize());
|
||||
dataSource.setMaxActive(haDataSource.getMaxActive());
|
||||
dataSource.setMinIdle(haDataSource.getMinIdle());
|
||||
dataSource.setMaxWait(haDataSource.getMaxWait());
|
||||
|
||||
dataSource.setValidationQuery(haDataSource.getValidationQuery());
|
||||
dataSource.setValidationQueryTimeout(haDataSource.getValidationQueryTimeout());
|
||||
dataSource.setTestOnBorrow(haDataSource.isTestOnBorrow());
|
||||
dataSource.setTestOnReturn(haDataSource.isTestOnReturn());
|
||||
dataSource.setTestWhileIdle(haDataSource.isTestWhileIdle());
|
||||
|
||||
dataSource.setPoolPreparedStatements(haDataSource.isPoolPreparedStatements());
|
||||
dataSource.setSharePreparedStatements(haDataSource.isSharePreparedStatements());
|
||||
dataSource.setMaxPoolPreparedStatementPerConnectionSize(
|
||||
haDataSource.getMaxPoolPreparedStatementPerConnectionSize());
|
||||
|
||||
dataSource.setQueryTimeout(haDataSource.getQueryTimeout());
|
||||
dataSource.setTransactionQueryTimeout(haDataSource.getTransactionQueryTimeout());
|
||||
|
||||
dataSource.setTimeBetweenEvictionRunsMillis(haDataSource.getTimeBetweenEvictionRunsMillis());
|
||||
dataSource.setMinEvictableIdleTimeMillis(haDataSource.getMinEvictableIdleTimeMillis());
|
||||
dataSource.setMaxEvictableIdleTimeMillis(haDataSource.getMaxEvictableIdleTimeMillis());
|
||||
dataSource.setPhyTimeoutMillis(haDataSource.getPhyTimeoutMillis());
|
||||
dataSource.setTimeBetweenConnectErrorMillis(haDataSource.getTimeBetweenConnectErrorMillis());
|
||||
|
||||
dataSource.setRemoveAbandoned(haDataSource.isRemoveAbandoned());
|
||||
dataSource.setRemoveAbandonedTimeoutMillis(haDataSource.getRemoveAbandonedTimeoutMillis());
|
||||
dataSource.setLogAbandoned(haDataSource.isLogAbandoned());
|
||||
|
||||
dataSource.setProxyFilters(haDataSource.getProxyFilters());
|
||||
dataSource.setFilters(haDataSource.getFilters());
|
||||
dataSource.setLogWriter(haDataSource.getLogWriter());
|
||||
|
||||
dataSource.init();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private void loadNameList() {
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (String n : properties.stringPropertyNames()) {
|
||||
if (n.contains(".")) {
|
||||
names.add(n.split("\\.")[0]);
|
||||
}
|
||||
}
|
||||
if (!names.isEmpty()) {
|
||||
nameList.addAll(names);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadProperties(String file) {
|
||||
Properties properties = new Properties();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
LOG.debug("Trying to load " + file + " from FileSystem.");
|
||||
is = new FileInputStream(file);
|
||||
} catch(FileNotFoundException e) {
|
||||
LOG.debug("Trying to load " + file + " from Classpath.");
|
||||
try {
|
||||
is = DataSourceCreator.class.getResourceAsStream(file);
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Can not load resource " + file, ex);
|
||||
}
|
||||
}
|
||||
if (is != null) {
|
||||
try {
|
||||
properties.load(is);
|
||||
this.properties = properties;
|
||||
} catch(Exception e) {
|
||||
LOG.error("Exception occurred while loading " + file, e);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.warn("File " + file + " can't be loaded!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
package com.alibaba.druid.pool.ha;
|
||||
|
||||
import com.alibaba.druid.filter.Filter;
|
||||
import com.alibaba.druid.pool.DruidAbstractDataSource;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.WrapperAdapter;
|
||||
import com.alibaba.druid.pool.ha.selector.DataSourceSelector;
|
||||
import com.alibaba.druid.pool.ha.selector.DataSourceSelectorFactory;
|
||||
import com.alibaba.druid.pool.ha.selector.RandomDataSourceSelector;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* DataSource class which contains multiple DataSource objects.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class HighAvailableDataSource extends WrapperAdapter implements DataSource {
|
||||
private final static Log LOG = LogFactory.getLog(HighAvailableDataSource.class);
|
||||
private final static String DEFAULT_DATA_SOURCE_FILE = "ha-datasource.properties";
|
||||
|
||||
// Properties copied from DruidAbstractDataSource BEGIN
|
||||
private String driverClassName;
|
||||
private Properties connectProperties = new Properties();
|
||||
private String connectionProperties = null;
|
||||
|
||||
private int initialSize = DruidAbstractDataSource.DEFAULT_INITIAL_SIZE;
|
||||
private int maxActive = DruidAbstractDataSource.DEFAULT_MAX_ACTIVE_SIZE;
|
||||
private int minIdle = DruidAbstractDataSource.DEFAULT_MIN_IDLE;
|
||||
private long maxWait = DruidAbstractDataSource.DEFAULT_MAX_WAIT;
|
||||
|
||||
private String validationQuery = DruidAbstractDataSource.DEFAULT_VALIDATION_QUERY;
|
||||
private int validationQueryTimeout = -1;
|
||||
private boolean testOnBorrow = DruidAbstractDataSource.DEFAULT_TEST_ON_BORROW;
|
||||
private boolean testOnReturn = DruidAbstractDataSource.DEFAULT_TEST_ON_RETURN;
|
||||
private boolean testWhileIdle = DruidAbstractDataSource.DEFAULT_WHILE_IDLE;
|
||||
|
||||
private boolean poolPreparedStatements = false;
|
||||
private boolean sharePreparedStatements = false;
|
||||
private int maxPoolPreparedStatementPerConnectionSize = 10;
|
||||
|
||||
private int queryTimeout;
|
||||
private int transactionQueryTimeout;
|
||||
|
||||
private long timeBetweenEvictionRunsMillis = DruidAbstractDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
|
||||
private long minEvictableIdleTimeMillis = DruidAbstractDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
|
||||
private long maxEvictableIdleTimeMillis = DruidAbstractDataSource.DEFAULT_MAX_EVICTABLE_IDLE_TIME_MILLIS;
|
||||
private long phyTimeoutMillis = DruidAbstractDataSource.DEFAULT_PHY_TIMEOUT_MILLIS;
|
||||
private long timeBetweenConnectErrorMillis = DruidAbstractDataSource.DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS;
|
||||
|
||||
private boolean removeAbandoned;
|
||||
private long removeAbandonedTimeoutMillis = 300 * 1000;
|
||||
private boolean logAbandoned;
|
||||
|
||||
private String filters;
|
||||
private List<Filter> proxyFilters = new CopyOnWriteArrayList<Filter>();
|
||||
private PrintWriter logWriter = new PrintWriter(System.out);
|
||||
// Properties copied from DruidAbstractDataSource END
|
||||
|
||||
private Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<String, DataSource>();
|
||||
private DataSourceSelector selector = new RandomDataSourceSelector(this);
|
||||
private String dataSourceFile = DEFAULT_DATA_SOURCE_FILE;
|
||||
|
||||
public void init() throws SQLException {
|
||||
synchronized (this) {
|
||||
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
|
||||
dataSourceMap = new DataSourceCreator(dataSourceFile).createMap(this);
|
||||
}
|
||||
if (selector == null) {
|
||||
selector = new RandomDataSourceSelector(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (DataSource dataSource : dataSourceMap.values()) {
|
||||
if (dataSource instanceof DruidDataSource) {
|
||||
((DruidDataSource) dataSource).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTargetDataSource(String targetName) {
|
||||
selector.setTarget(targetName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
DataSource dataSource = selector.get();
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
public String getDataSourceFile() {
|
||||
return dataSourceFile;
|
||||
}
|
||||
|
||||
public void setDataSourceFile(String dataSourceFile) {
|
||||
this.dataSourceFile = dataSourceFile;
|
||||
}
|
||||
|
||||
public void setDataSourceMap(Map<String, DataSource> dataSourceMap) {
|
||||
if (dataSourceMap != null) {
|
||||
this.dataSourceMap = dataSourceMap;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, DataSource> getDataSourceMap() {
|
||||
return dataSourceMap;
|
||||
}
|
||||
|
||||
public void setSelector(String name) {
|
||||
if (name != null && this.selector != null && this.selector.isSame(name)) {
|
||||
return;
|
||||
}
|
||||
DataSourceSelector selector = DataSourceSelectorFactory.getSelector(name, this);
|
||||
if (selector != null) {
|
||||
this.selector = selector;
|
||||
}
|
||||
}
|
||||
|
||||
public DataSourceSelector getSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(String username, String password) throws SQLException {
|
||||
throw new UnsupportedOperationException("Not supported by DruidDataSource");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return logWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
this.logWriter = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) {
|
||||
DriverManager.setLoginTimeout(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() {
|
||||
return DriverManager.getLoginTimeout();
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
|
||||
public String getDriverClassName() {
|
||||
return driverClassName;
|
||||
}
|
||||
|
||||
public void setDriverClassName(String driverClassName) {
|
||||
this.driverClassName = driverClassName;
|
||||
}
|
||||
|
||||
public Properties getConnectProperties() {
|
||||
return connectProperties;
|
||||
}
|
||||
|
||||
public void setConnectProperties(Properties connectProperties) {
|
||||
this.connectProperties = connectProperties;
|
||||
}
|
||||
|
||||
public int getInitialSize() {
|
||||
return initialSize;
|
||||
}
|
||||
|
||||
public void setInitialSize(int initialSize) {
|
||||
this.initialSize = initialSize;
|
||||
}
|
||||
|
||||
public int getMaxActive() {
|
||||
return maxActive;
|
||||
}
|
||||
|
||||
public void setMaxActive(int maxActive) {
|
||||
this.maxActive = maxActive;
|
||||
}
|
||||
|
||||
public int getMinIdle() {
|
||||
return minIdle;
|
||||
}
|
||||
|
||||
public void setMinIdle(int minIdle) {
|
||||
this.minIdle = minIdle;
|
||||
}
|
||||
|
||||
public long getMaxWait() {
|
||||
return maxWait;
|
||||
}
|
||||
|
||||
public void setMaxWait(long maxWait) {
|
||||
this.maxWait = maxWait;
|
||||
}
|
||||
|
||||
public String getValidationQuery() {
|
||||
return validationQuery;
|
||||
}
|
||||
|
||||
public void setValidationQuery(String validationQuery) {
|
||||
this.validationQuery = validationQuery;
|
||||
}
|
||||
|
||||
public int getValidationQueryTimeout() {
|
||||
return validationQueryTimeout;
|
||||
}
|
||||
|
||||
public void setValidationQueryTimeout(int validationQueryTimeout) {
|
||||
this.validationQueryTimeout = validationQueryTimeout;
|
||||
}
|
||||
|
||||
public boolean isTestOnBorrow() {
|
||||
return testOnBorrow;
|
||||
}
|
||||
|
||||
public void setTestOnBorrow(boolean testOnBorrow) {
|
||||
this.testOnBorrow = testOnBorrow;
|
||||
}
|
||||
|
||||
public boolean isTestOnReturn() {
|
||||
return testOnReturn;
|
||||
}
|
||||
|
||||
public void setTestOnReturn(boolean testOnReturn) {
|
||||
this.testOnReturn = testOnReturn;
|
||||
}
|
||||
|
||||
public boolean isTestWhileIdle() {
|
||||
return testWhileIdle;
|
||||
}
|
||||
|
||||
public void setTestWhileIdle(boolean testWhileIdle) {
|
||||
this.testWhileIdle = testWhileIdle;
|
||||
}
|
||||
|
||||
public boolean isPoolPreparedStatements() {
|
||||
return poolPreparedStatements;
|
||||
}
|
||||
|
||||
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
|
||||
this.poolPreparedStatements = poolPreparedStatements;
|
||||
}
|
||||
|
||||
public boolean isSharePreparedStatements() {
|
||||
return sharePreparedStatements;
|
||||
}
|
||||
|
||||
public void setSharePreparedStatements(boolean sharePreparedStatements) {
|
||||
this.sharePreparedStatements = sharePreparedStatements;
|
||||
}
|
||||
|
||||
public int getMaxPoolPreparedStatementPerConnectionSize() {
|
||||
return maxPoolPreparedStatementPerConnectionSize;
|
||||
}
|
||||
|
||||
public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
|
||||
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
|
||||
}
|
||||
|
||||
public int getQueryTimeout() {
|
||||
return queryTimeout;
|
||||
}
|
||||
|
||||
public void setQueryTimeout(int queryTimeout) {
|
||||
this.queryTimeout = queryTimeout;
|
||||
}
|
||||
|
||||
public int getTransactionQueryTimeout() {
|
||||
return transactionQueryTimeout;
|
||||
}
|
||||
|
||||
public void setTransactionQueryTimeout(int transactionQueryTimeout) {
|
||||
this.transactionQueryTimeout = transactionQueryTimeout;
|
||||
}
|
||||
|
||||
public long getTimeBetweenEvictionRunsMillis() {
|
||||
return timeBetweenEvictionRunsMillis;
|
||||
}
|
||||
|
||||
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
|
||||
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
|
||||
}
|
||||
|
||||
public long getMinEvictableIdleTimeMillis() {
|
||||
return minEvictableIdleTimeMillis;
|
||||
}
|
||||
|
||||
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
|
||||
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
|
||||
}
|
||||
|
||||
public long getMaxEvictableIdleTimeMillis() {
|
||||
return maxEvictableIdleTimeMillis;
|
||||
}
|
||||
|
||||
public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
|
||||
this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
|
||||
}
|
||||
|
||||
public long getPhyTimeoutMillis() {
|
||||
return phyTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setPhyTimeoutMillis(long phyTimeoutMillis) {
|
||||
this.phyTimeoutMillis = phyTimeoutMillis;
|
||||
}
|
||||
|
||||
public long getTimeBetweenConnectErrorMillis() {
|
||||
return timeBetweenConnectErrorMillis;
|
||||
}
|
||||
|
||||
public void setTimeBetweenConnectErrorMillis(long timeBetweenConnectErrorMillis) {
|
||||
this.timeBetweenConnectErrorMillis = timeBetweenConnectErrorMillis;
|
||||
}
|
||||
|
||||
public boolean isRemoveAbandoned() {
|
||||
return removeAbandoned;
|
||||
}
|
||||
|
||||
public void setRemoveAbandoned(boolean removeAbandoned) {
|
||||
this.removeAbandoned = removeAbandoned;
|
||||
}
|
||||
|
||||
public long getRemoveAbandonedTimeoutMillis() {
|
||||
return removeAbandonedTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setRemoveAbandonedTimeoutMillis(long removeAbandonedTimeoutMillis) {
|
||||
this.removeAbandonedTimeoutMillis = removeAbandonedTimeoutMillis;
|
||||
}
|
||||
|
||||
public boolean isLogAbandoned() {
|
||||
return logAbandoned;
|
||||
}
|
||||
|
||||
public void setLogAbandoned(boolean logAbandoned) {
|
||||
this.logAbandoned = logAbandoned;
|
||||
}
|
||||
|
||||
public String getConnectionProperties() {
|
||||
return connectionProperties;
|
||||
}
|
||||
|
||||
public void setConnectionProperties(String connectionProperties) {
|
||||
this.connectionProperties = connectionProperties;
|
||||
}
|
||||
|
||||
public String getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void setFilters(String filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public List<Filter> getProxyFilters() {
|
||||
return proxyFilters;
|
||||
}
|
||||
|
||||
public void setProxyFilters(List<Filter> proxyFilters) {
|
||||
this.proxyFilters = proxyFilters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for those selector to implement.
|
||||
* e.g. Random and Named
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public interface DataSourceSelector {
|
||||
DataSource get();
|
||||
void setTarget(String name);
|
||||
boolean isSame(String name);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
|
||||
/**
|
||||
* A Factory pattern for DataSourceSelector.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class DataSourceSelectorFactory {
|
||||
public static DataSourceSelector getSelector(String name, HighAvailableDataSource highAvailableDataSource) {
|
||||
if ("random".equalsIgnoreCase(name)) {
|
||||
return new RandomDataSourceSelector(highAvailableDataSource);
|
||||
} else if ("byName".equalsIgnoreCase(name)) {
|
||||
return new NamedDataSourceSelector(highAvailableDataSource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Use the given name in ThreadLocal variable to choose DataSource.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class NamedDataSourceSelector implements DataSourceSelector {
|
||||
public static final String DEFAULT_NAME = "default";
|
||||
private HighAvailableDataSource highAvailableDataSource;
|
||||
private ThreadLocal<String> targetDataSourceName = new ThreadLocal<String>();
|
||||
private String defaultName = DEFAULT_NAME;
|
||||
|
||||
public NamedDataSourceSelector(HighAvailableDataSource highAvailableDataSource) {
|
||||
this.highAvailableDataSource = highAvailableDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(String name) {
|
||||
return "byName".equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource get() {
|
||||
if (highAvailableDataSource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, DataSource> dataSourceMap = highAvailableDataSource.getDataSourceMap();
|
||||
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (dataSourceMap.size() == 1) {
|
||||
for (DataSource v : dataSourceMap.values()) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
String name = getTarget();
|
||||
if (name == null) {
|
||||
if (dataSourceMap.get(getDefaultName()) != null) {
|
||||
return dataSourceMap.get(getDefaultName());
|
||||
}
|
||||
} else {
|
||||
return dataSourceMap.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(String name) {
|
||||
targetDataSourceName.set(name);
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return targetDataSourceName.get();
|
||||
}
|
||||
|
||||
public void resetDataSourceName() {
|
||||
targetDataSourceName.remove();
|
||||
}
|
||||
|
||||
public String getDefaultName() {
|
||||
return defaultName;
|
||||
}
|
||||
|
||||
public void setDefaultName(String defaultName) {
|
||||
this.defaultName = defaultName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* A Thread trying to test if DataSource in blacklist has been recovered.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class RandomDataSourceRecoverThread implements Runnable {
|
||||
private final static Log LOG = LogFactory.getLog(RandomDataSourceRecoverThread.class);
|
||||
private RandomDataSourceSelector selector;
|
||||
private int sleepSeconds = 30;
|
||||
|
||||
public RandomDataSourceRecoverThread(RandomDataSourceSelector selector) {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
if (selector != null && selector.getBlacklist() != null
|
||||
&& !selector.getBlacklist().isEmpty()) {
|
||||
LOG.info(selector.getBlacklist().size() + " DataSource in blacklist.");
|
||||
for (DataSource dataSource : selector.getBlacklist()) {
|
||||
if (!(dataSource instanceof DruidDataSource)) {
|
||||
continue;
|
||||
}
|
||||
tryOneDataSource((DruidDataSource) dataSource);
|
||||
}
|
||||
}
|
||||
sleep();
|
||||
}
|
||||
}
|
||||
|
||||
private void tryOneDataSource(DruidDataSource dataSource) {
|
||||
if (dataSource == null) {
|
||||
return;
|
||||
}
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
dataSource.validateConnection(connection);
|
||||
LOG.info(dataSource.getName() + " is available now.");
|
||||
selector.removeBlacklist(dataSource);
|
||||
} catch(Exception e) {
|
||||
LOG.warn("DataSource[" + dataSource.getName() + "] is still unavailable. Exception: "
|
||||
+ e.getMessage());
|
||||
if (connection != null) {
|
||||
dataSource.discardConnection(connection);
|
||||
}
|
||||
} finally {
|
||||
JdbcUtils.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(sleepSeconds * 1000);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void setSleepSeconds(int sleepSeconds) {
|
||||
this.sleepSeconds = sleepSeconds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* A selector which uses java.util.Random to choose DataSource.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class RandomDataSourceSelector implements DataSourceSelector {
|
||||
private Random random = new Random();
|
||||
private List<DataSource> blacklist = new CopyOnWriteArrayList<DataSource>();
|
||||
private HighAvailableDataSource highAvailableDataSource;
|
||||
private RandomDataSourceValidateThread validateThread;
|
||||
private RandomDataSourceRecoverThread recoverThread;
|
||||
|
||||
public RandomDataSourceSelector(HighAvailableDataSource highAvailableDataSource) {
|
||||
this.highAvailableDataSource = highAvailableDataSource;
|
||||
validateThread = new RandomDataSourceValidateThread(this);
|
||||
recoverThread = new RandomDataSourceRecoverThread(this);
|
||||
new Thread(validateThread, "RandomDataSourceSelector-validate-thread").start();
|
||||
new Thread(recoverThread, "RandomDataSourceSelector-recover-thread").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(String name) {
|
||||
return "random".equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSource get() {
|
||||
Map<String, DataSource> dataSourceMap = getDataSourceMap();
|
||||
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Collection<DataSource> targetDataSourceSet;
|
||||
if (blacklist == null || blacklist.isEmpty() || blacklist.size() >= dataSourceMap.size()) {
|
||||
targetDataSourceSet = dataSourceMap.values();
|
||||
} else {
|
||||
targetDataSourceSet = new HashSet<DataSource>(dataSourceMap.values());
|
||||
for (DataSource b : blacklist) {
|
||||
targetDataSourceSet.remove(b);
|
||||
}
|
||||
}
|
||||
|
||||
DataSource[] dataSources = targetDataSourceSet.toArray(new DataSource[] {});
|
||||
if (dataSources != null && dataSources.length > 0) {
|
||||
return dataSources[random.nextInt(targetDataSourceSet.size())];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(String name) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public Map<String, DataSource> getDataSourceMap() {
|
||||
if (highAvailableDataSource != null) {
|
||||
return highAvailableDataSource.getDataSourceMap();
|
||||
}
|
||||
return new HashMap<String, DataSource>();
|
||||
}
|
||||
|
||||
public List<DataSource> getBlacklist() {
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
public void addBlacklist(DataSource dataSource) {
|
||||
if (dataSource != null && !blacklist.contains(dataSource)) {
|
||||
blacklist.add(dataSource);
|
||||
if (dataSource instanceof DruidDataSource) {
|
||||
((DruidDataSource) dataSource).setTestOnReturn(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeBlacklist(DataSource dataSource) {
|
||||
if (dataSource != null && blacklist.contains(dataSource)) {
|
||||
blacklist.remove(dataSource);
|
||||
if (dataSource instanceof DruidDataSource) {
|
||||
((DruidDataSource) dataSource).setTestOnReturn(highAvailableDataSource.isTestOnReturn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RandomDataSourceValidateThread getValidateThread() {
|
||||
return validateThread;
|
||||
}
|
||||
|
||||
public RandomDataSourceRecoverThread getRecoverThread() {
|
||||
return recoverThread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* A Thread trying to test all DataSource provided by HADataSource.
|
||||
* If a DataSource failed this test for 3 times, it will be put into a blacklist.
|
||||
*
|
||||
* @author DigitalSonic
|
||||
*/
|
||||
public class RandomDataSourceValidateThread implements Runnable {
|
||||
private final static Log LOG = LogFactory.getLog(RandomDataSourceValidateThread.class);
|
||||
private int sleepSeconds = 30;
|
||||
private int blacklistThreshold = 3;
|
||||
|
||||
private RandomDataSourceSelector selector;
|
||||
private Map<String, Integer> errorCounts = new HashMap<String, Integer>();
|
||||
|
||||
public RandomDataSourceValidateThread(RandomDataSourceSelector selector) {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
if (selector != null) {
|
||||
checkAllDataSources();
|
||||
maintainBlacklist();
|
||||
}
|
||||
try {
|
||||
Thread.sleep(sleepSeconds * 1000);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maintainBlacklist() {
|
||||
Map<String, DataSource> dataSourceMap = selector.getDataSourceMap();
|
||||
for (Map.Entry<String, Integer> e : errorCounts.entrySet()) {
|
||||
if (e.getValue() <= 0) {
|
||||
selector.removeBlacklist(dataSourceMap.get(e.getKey()));
|
||||
} else if (e.getValue() >= blacklistThreshold) {
|
||||
LOG.warn("Adding " + e.getKey() + " to blacklist.");
|
||||
selector.addBlacklist(dataSourceMap.get(e.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAllDataSources() {
|
||||
Map<String, DataSource> dataSourceMap = selector.getDataSourceMap();
|
||||
for (Map.Entry<String, DataSource> e : dataSourceMap.entrySet()) {
|
||||
if (!(e.getValue() instanceof DruidDataSource)) {
|
||||
continue;
|
||||
}
|
||||
boolean flag = check(e.getKey(), (DruidDataSource) e.getValue());
|
||||
|
||||
if (flag) {
|
||||
errorCounts.put(e.getKey(), 0);
|
||||
} else {
|
||||
if (!errorCounts.containsKey(e.getKey())) {
|
||||
errorCounts.put(e.getKey(), 0);
|
||||
}
|
||||
int count = errorCounts.get(e.getKey());
|
||||
errorCounts.put(e.getKey(), count + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean check(String name, DruidDataSource dataSource) {
|
||||
boolean result = true;
|
||||
Driver driver = dataSource.getRawDriver();
|
||||
Properties info = dataSource.getConnectProperties();
|
||||
String url = dataSource.getRawJdbcUrl();
|
||||
Connection conn = null;
|
||||
|
||||
try {
|
||||
LOG.debug("Validating " + name + " every " + sleepSeconds + " seconds.");
|
||||
conn = driver.connect(url, info);
|
||||
dataSource.validateConnection(conn);
|
||||
} catch (SQLException e) {
|
||||
LOG.warn("Validation FAILED for " + name + ". Exception: " + e.getMessage());
|
||||
result = false;
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
LOG.error("Can not close connection for HostAndPort Validation.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSleepSeconds(int sleepSeconds) {
|
||||
this.sleepSeconds = sleepSeconds;
|
||||
}
|
||||
|
||||
public void setBlacklistThreshold(int blacklistThreshold) {
|
||||
this.blacklistThreshold = blacklistThreshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.alibaba.druid.pool.ha;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DataSourceCreatorTest {
|
||||
@Test
|
||||
public void createMap() throws Exception {
|
||||
HighAvailableDataSource highAvailableDataSource = new HighAvailableDataSource();
|
||||
String file = "/com/alibaba/druid/pool/ha/ha-datasource.properties";
|
||||
DataSourceCreator creator = new DataSourceCreator(file);
|
||||
Map<String, DataSource> map = creator.createMap(highAvailableDataSource);
|
||||
assertEquals(2, map.size());
|
||||
assertNotNull(map.get("foo"));
|
||||
assertNotNull(map.get("bar"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.alibaba.druid.pool.ha;
|
||||
|
||||
import com.alibaba.druid.mock.MockConnection;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class HighAvailableDataSourceTest {
|
||||
@Test
|
||||
public void testByNameGet() throws Exception {
|
||||
HighAvailableDataSource haDataSource = new HighAvailableDataSource();
|
||||
Map<String, DataSource> map = new HashMap<String, DataSource>();
|
||||
map.put("foo", new MockDataSource("foo"));
|
||||
map.put("bar", new MockDataSource("bar"));
|
||||
haDataSource.setDataSourceMap(map);
|
||||
haDataSource.setSelector("byname");
|
||||
haDataSource.init();
|
||||
haDataSource.setTargetDataSource("foo");
|
||||
assertNotNull(haDataSource.getConnection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomGet() throws Exception {
|
||||
HighAvailableDataSource haDataSource = new HighAvailableDataSource();
|
||||
Map<String, DataSource> map = new HashMap<String, DataSource>();
|
||||
map.put("foo", new MockDataSource("foo"));
|
||||
map.put("bar", new MockDataSource("bar"));
|
||||
int[] count = new int[2];
|
||||
|
||||
haDataSource.setDataSourceMap(map);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
String name = ((MockConnection)haDataSource.getConnection()).getUrl();
|
||||
if (name.equalsIgnoreCase("foo")) {
|
||||
count[0]++;
|
||||
} else if (name.equalsIgnoreCase("bar")) {
|
||||
count[1]++;
|
||||
}
|
||||
}
|
||||
assertTrue(count[0] > 0);
|
||||
assertTrue(count[1] > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.alibaba.druid.pool.ha;
|
||||
|
||||
import com.alibaba.druid.mock.MockConnection;
|
||||
import com.alibaba.druid.pool.WrapperAdapter;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MockDataSource extends WrapperAdapter implements DataSource {
|
||||
private String name;
|
||||
|
||||
public MockDataSource(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return new MockConnection(null, name, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection(String username, String password) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
import com.alibaba.druid.pool.ha.MockDataSource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class NamedDataSourceSelectorTest {
|
||||
private Map<String, DataSource> dataSourceMap;
|
||||
private HighAvailableDataSource dataSource;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
dataSourceMap = new HashMap<String, DataSource>();
|
||||
dataSourceMap.put("foo", new MockDataSource("foo"));
|
||||
dataSourceMap.put("bar", new MockDataSource("bar"));
|
||||
dataSource = new HighAvailableDataSource();
|
||||
dataSource.setDataSourceMap(dataSourceMap);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
dataSourceMap = null;
|
||||
dataSource = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMap() {
|
||||
dataSourceMap.clear();
|
||||
NamedDataSourceSelector selector = new NamedDataSourceSelector(null);
|
||||
assertNull(selector.get());
|
||||
selector = new NamedDataSourceSelector(dataSource);
|
||||
assertNull(selector.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyOne() {
|
||||
dataSourceMap.remove("foo");
|
||||
NamedDataSourceSelector selector = new NamedDataSourceSelector(dataSource);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertEquals("bar", ((MockDataSource)selector.get()).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByName() throws Exception {
|
||||
NamedDataSourceSelector selector = new NamedDataSourceSelector(dataSource);
|
||||
|
||||
assertNull(selector.get());
|
||||
selector.setTarget("foo");
|
||||
assertEquals("foo", ((MockDataSource)selector.get()).getName());
|
||||
selector.resetDataSourceName();
|
||||
selector.setDefaultName("bar");
|
||||
assertEquals("bar", ((MockDataSource)selector.get()).getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
import com.alibaba.druid.pool.ha.MockDataSource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RandomDataSourceSelectorTest {
|
||||
private Map<String, DataSource> dataSourceMap;
|
||||
private HighAvailableDataSource dataSource;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
dataSourceMap = new HashMap<String, DataSource>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
dataSourceMap.put(Integer.toString(i), new MockDataSource(Integer.toString(i)));
|
||||
}
|
||||
dataSource = new HighAvailableDataSource();
|
||||
dataSource.setDataSourceMap(dataSourceMap);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
dataSourceMap = null;
|
||||
dataSource = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomGet() throws Exception {
|
||||
RandomDataSourceSelector selector = new RandomDataSourceSelector(dataSource);
|
||||
int[] count = new int[10];
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
MockDataSource dataSource = (MockDataSource) selector.get();
|
||||
count[Integer.parseInt(dataSource.getName())]++;
|
||||
}
|
||||
|
||||
for (int i : count) {
|
||||
assertTrue(i > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneDataSourceFail() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.alibaba.druid.pool.ha.selector;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.ha.HighAvailableDataSource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class RandomDataSourceSelectorWithValidationTest {
|
||||
private HighAvailableDataSource highAvailableDataSource;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
highAvailableDataSource = new HighAvailableDataSource();
|
||||
String file = "/com/alibaba/druid/pool/ha/ha-datasource.properties";
|
||||
highAvailableDataSource.setDataSourceFile(file);
|
||||
highAvailableDataSource.init();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
highAvailableDataSource = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneDataSourceFailAndRecover() throws Exception {
|
||||
((RandomDataSourceSelector) highAvailableDataSource.getSelector()).getValidateThread().setSleepSeconds(3);
|
||||
((RandomDataSourceSelector) highAvailableDataSource.getSelector()).getRecoverThread().setSleepSeconds(3);
|
||||
|
||||
Thread.sleep(30 * 1000);
|
||||
|
||||
DruidDataSource dataSource = (DruidDataSource) highAvailableDataSource.getDataSourceMap().get("foo");
|
||||
dataSource.setValidationQuery("select xxx from yyy");
|
||||
Thread.sleep(10 * 1000);
|
||||
assertTrue(dataSource.isTestOnReturn());
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertNotEquals(dataSource, highAvailableDataSource.getSelector().get());
|
||||
}
|
||||
|
||||
dataSource.setValidationQuery(null);
|
||||
Thread.sleep(4 * 1000);
|
||||
assertFalse(dataSource.isTestOnReturn());
|
||||
int count = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (dataSource == highAvailableDataSource.getSelector().get()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assertTrue(count > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllDataSourceFail() throws Exception {
|
||||
((RandomDataSourceSelector) highAvailableDataSource.getSelector()).getValidateThread().setSleepSeconds(3);
|
||||
((RandomDataSourceSelector) highAvailableDataSource.getSelector()).getRecoverThread().setSleepSeconds(3);
|
||||
|
||||
Thread.sleep(30 * 1000);
|
||||
|
||||
DruidDataSource foo = (DruidDataSource) highAvailableDataSource.getDataSourceMap().get("foo");
|
||||
DruidDataSource bar = (DruidDataSource) highAvailableDataSource.getDataSourceMap().get("bar");
|
||||
foo.setValidationQuery("select xxx from yyy");
|
||||
bar.setValidationQuery("select xxx from yyy");
|
||||
|
||||
Thread.sleep(10 * 1000);
|
||||
assertTrue(foo.isTestOnReturn());
|
||||
assertTrue(bar.isTestOnReturn());
|
||||
|
||||
int[] count = new int[2];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
DataSource dataSource = highAvailableDataSource.getSelector().get();
|
||||
if (foo == dataSource) {
|
||||
count[0]++;
|
||||
} else if (bar == dataSource) {
|
||||
count[1]++;
|
||||
}
|
||||
}
|
||||
assertTrue(count[0] > 0);
|
||||
assertTrue(count[1] > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
foo.url=jdbc:derby:memory:foo;create=true
|
||||
bar.url=jdbc:derby:memory:bar;create=true
|
||||
Reference in New Issue
Block a user