思路
繼承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 重寫determineCurrentLookupKey方法
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
public class MyDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return null;
}
}
複製程式碼
例子
實現
主要涉及兩個類AbstractRoutingDataSource和DataSourceHolder
AbstractRoutingDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource{
private final Logger logger = LoggerFactory.getLogger(getClass());
@Inject
private DataSourceManagerService dataSourceManagerService;
@Inject
@Qualifier("defaultCpsDataSource")
private DataSource defaultCpsDataSource;
@Inject
private CpsDbProp cpsDbProp;
@Inject
private SysProp sysProp;
@Inject
private Caches caches;
private Map<String, DruidDataSource> targetDataSources = new ConcurrentHashMap<>();
@Override
protected Object determineCurrentLookupKey() {
String dsId = DataSourceHolder.getDataSource();
logger.debug("[{}]連線到的資料來源id為:[{}]", Thread.currentThread().getName(), dsId);
if(null == dsId){
return null;
}
return getTargetDataSource(dsId);
}
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources basicDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
@Override
protected DataSource determineTargetDataSource() {
logger.debug("dynamicDataSource={}", this);
DataSource dataSource = (DataSource) determineCurrentLookupKey();
if (dataSource == null) {
dataSource = defaultCpsDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource");
}
logger.debug("dataSource={}", dataSource);
return dataSource;
}
/**
* 獲取目標資料來源
* @param dsId
* @return
*/
private DruidDataSource getTargetDataSource(String dsId) {
if(null == dsId){
return null;
}
if(Constants.Cache.ENABLE_CACHE.equals(sysProp.redisCacheEnable)){
return getTargetDataSourceWithCache(dsId);
}
return getTargetDataSourceWithoutCache(dsId);
}
private DruidDataSource getTargetDataSourceWithoutCache(String dsId) {
DruidDataSource druidDataSource = targetDataSources.get(dsId);
if (druidDataSource != null) return druidDataSource;
synchronized (this) {
druidDataSource = targetDataSources.get(dsId);
if (druidDataSource != null) return druidDataSource;
DataSourceRuntimeMeta dataSourceRuntimeMeta = dataSourceManagerService.getDataSourceRuntimeMeta(dsId);
druidDataSource = buildBasicDataSource(dsId, dataSourceRuntimeMeta);
}
return druidDataSource;
}
//資料來源修改時怎麼做? 去拿一次快取,快取存在的話則獲取,否則更新
private DruidDataSource getTargetDataSourceWithCache(String dsId){
DruidDataSource druidDataSource;
String obj = caches.getThenSetExpired(String.format(Constants.Cache.DATA_SOURCE, dsId));
if(obj != null){
druidDataSource = targetDataSources.get(dsId);
if(druidDataSource != null) return druidDataSource;
}
synchronized (this) {
obj = caches.getThenSetExpired(String.format(Constants.Cache.DATA_SOURCE, dsId));
if(obj != null){
druidDataSource = targetDataSources.get(dsId);
if(druidDataSource != null) return druidDataSource;
}
targetDataSources.remove(dsId);
DataSourceRuntimeMeta dataSourceRuntimeMeta = dataSourceManagerService.getDataSourceRuntimeMeta(dsId);
druidDataSource = buildBasicDataSource(dsId, dataSourceRuntimeMeta);
}
return druidDataSource;
}
private DruidDataSource buildBasicDataSource(String dsId, DataSourceRuntimeMeta dataSourceRuntimeMeta) {
if(null == dataSourceRuntimeMeta){
logger.warn("DataSourceRuntimeMeta is null, dsId=[{}],請確認該資料來源是否存在或者停用!", dsId);
return null;
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername(dataSourceRuntimeMeta.getUserName());
druidDataSource.setPassword(dataSourceRuntimeMeta.getPassword());
druidDataSource.setUrl(dataSourceRuntimeMeta.getDbParameter());
druidDataSource.setDriverClassName(cpsDbProp.driver);
//druidDataSource.setMaxIdle(cpsDbProp.maxIdle);/*druid已經不再使用,配置了也沒效果*/
druidDataSource.setMinIdle(cpsDbProp.minIdle);
druidDataSource.setMaxWait(cpsDbProp.maxWait);
druidDataSource.setValidationQuery(cpsDbProp.validateSql);
druidDataSource.setMinEvictableIdleTimeMillis(cpsDbProp.minEvictableIdleTimeMillis);
druidDataSource.setTimeBetweenEvictionRunsMillis(cpsDbProp.timeBetweenEvictionRunsMillis);
if(null != dataSourceRuntimeMeta.getMaxActive()){
druidDataSource.setMaxActive(dataSourceRuntimeMeta.getMaxActive());
}else {
druidDataSource.setMaxActive(cpsDbProp.maxActive);
}
if(null != dataSourceRuntimeMeta.getQueryTimeout()){
druidDataSource.setQueryTimeout(dataSourceRuntimeMeta.getQueryTimeout());
}else {
druidDataSource.setQueryTimeout(cpsDbProp.queryTimeout);
}
druidDataSource.setValidationQuery(cpsDbProp.validateSql);
druidDataSource.setTestOnBorrow(true);
targetDataSources.putIfAbsent(dsId, druidDataSource);
return druidDataSource;
}
}
複製程式碼
DataSourceHolder.java
public class DataSourceHolder {
private static final Logger logger = LoggerFactory.getLogger(DataSourceHolder.class);
//用來存放執行緒資訊
private static final ThreadLocal<String> dsHolder = new ThreadLocal<>();
public static void setDataSource(String dsId){
logger.debug("[{}] setDataSource,dsId=[{}]", Thread.currentThread().getName(), dsId);
dsHolder.set(dsId);
}
public static String getDataSource(){
logger.debug("[{}] getDataSource", Thread.currentThread().getName());
return dsHolder.get();
}
public static void clearDataSource(){
logger.debug("[{}] clearDataSource", Thread.currentThread().getName());
dsHolder.remove();
}
}
複製程式碼
單元測試
public class DynamicDataSourceTest extends BaseTest {
ExecutorService executorService = Executors.newFixedThreadPool(20);
@Autowired
QueryTest queryTest;
@Inject
DataSourceManagerMapper dataSourceManagerMapper;
@Inject
private DsRunningStateCache dsRunningStateCache;
private int MAX_SIZE = 1;
private String[] dsIds = {"caa8ef43e1be475985d08c3fef873291","1","d5a87b23852543279b1a517dc408630b"};
@Test
public void test(){
CountDownLatch countDownLatch = new CountDownLatch(MAX_SIZE);
AtomicInteger atomicInteger = new AtomicInteger(0);
for(int i = 0;i < MAX_SIZE;++i){
executorService.execute(
()->{
int idx = atomicInteger.incrementAndGet();
String dsId = dsIds[idx % dsIds.length];
try{
DataSourceHolder.setDataSource(dsId);
dsRunningStateCache.setDsRunningState(dsId, DataSourceRunningState.RUNNING.getCode());
queryTest.queryTest3();
}catch (Exception e){
e.printStackTrace();
}
finally {
DataSourceHolder.clearDataSource();
dsRunningStateCache.setDsRunningState(dsId, DataSourceRunningState.STOP.getCode());
countDownLatch.countDown();
}
}
);
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製程式碼