
最近做了一个数据源平台,主要功能是它可以存在多个数据源在一个工程下,并且在执行sql时能找到正确的数据源切换并执行。也就是动态数据源切换执行。
原工程使用的是springJdbc + druid进行的数据源管理,因此数据源获取结构发生了如下改变:
核心代码就是实现 AbstractRoutingDataSource类determineCurrentLookupKey()方法。
原来工程使用的数据源也要被AbstractRoutingDataSource管理,在这里暂且称为主数据源用于区分。
依赖
org.springframework
spring-jdbc
5.2.8.RELEASE
com.baomidou
mybatis-plus
3.4.1
com.oracle.database.jdbc
ojdbc8
5.2.8.RELEASE
com.oracle.database.jdbc
ojdbc8
12.2.0.1
com.microsoft.sqlserver
mssql-jdbc
9.4.1.jre8
源码
AbstractRoutingDataSource类中实现的getConnection()方法
public Connection getConnection() throws SQLException {
return this.determineTargetDataSource().getConnection();
}
调用了determineTargetDataSource(),其内部又调用了唯一抽象方determineCurrentLookupKey(),因此得知这个方法需要返回具体key值,然后才能从resolvedDataSources这个map中取到DataSource,从而获取数据库连接。
private Map
代码
- 创建本地线程维护类,用于切换数据源
public class RoutingDataSourceContext {
private static final ThreadLocal threadLocalDataSourceKey = new ThreadLocal<>();
/**
* 获取主数据库的key
*
* @return
*/
public static String getMainKey() {
return "mainDB";
}
/**
* 获取数据库key
*
* @return
*/
public static String getDataSourceRoutingKey() {
String key = threadLocalDataSourceKey.get();
return key == null ? getMainKey() : key;
}
/**
* 设置数据库的key
*
* @param key
*/
public static void setThreadLocalDataSourceKey(String key) {
threadLocalDataSourceKey.set(key);
}
}
- 自定义类RoutingDataSource ,继承AbstractRoutingDataSource
@Configuration
@MapperScan("com.your.package.mapper")
public class RoutingDataSource extends AbstractRoutingDataSource
- 创建sqlSession工厂。这里一定要把this传入dataSource
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.apache.ibatis.session.SqlSessionFactory;
@Configuration
@MapperScan("com.your.package.mapper")
public class RoutingDataSource extends AbstractRoutingDataSource {
private Map
- 实现determineCurrentLookupKey()方法
@Override
//本地线程没有则使用主数据源
protected Object determineCurrentLookupKey() {
String dbSourceKey = RoutingDataSourceContext.getDataSourceRoutingKey();
if (StringUtils.isEmpty(dbSourceKey)) {
dbSourceKey = RoutingDataSourceContext.getMainKey();
}
log.info("当前 *** 作数据源ID:{}", dbSourceKey);
if (!dataSourcesMap.containsKey(dbSourceKey)) {
log.info("{}数据源不存在, 创建对应的数据源", dbSourceKey);
createAndSaveDataSource(dbSourceKey);
} else {
log.info("{}数据源已存在不需要创建", dbSourceKey);
}
log.info("切换到{}数据源", dbSourceKey);
return dbSourceKey;
}
private synchronized void createAndSaveDataSource(String dbSourceKey) {
DruidDataSource dataSource;
if (dbSourceKey.equals(RoutingDataSourceContext.getMainKey())) {
//创建主数据源,主数据源的durid配置要区别于其他
dataSource = getMainDBSource();
//设置默认的数据源
super.setDefaultTargetDataSource(dataSource);
//加入本地容器中
dataSourcesMap.put(dbSourceKey, dataSource);
} else {
dataSource = createDruidDataSource(dbSourceKey);
//在执行过程中不必调用druid.init(),在获取链接时自动调用
log.info("{}数据源创建成功", dbSourceKey);
//加入本地容器中
dataSourcesMap.put(dbSourceKey, dataSource);
//设置路由容器
super.setTargetDataSources(dataSourcesMap);
}
//对创建的数据源初始化
afterPropertiesSet();
}
private static DruidDataSource createDruidDataSource(DynamicDataSourcePO dataSourceBean) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(dataSourceBean.getUrl());
dataSource.setUsername(dataSourceBean.getUsername());
dataSource.setPassword(dataSourceBean.getPassword());
dataSource.setInitialSize(1);
dataSource.setMaxActive(2);
dataSource.setMinIdle(1);
dataSource.setTestOnBorrow(false);
dataSource.setValidationQuery("select 1");
// 如果连接空闲超过1小时就断开
dataSource.setMinEvictableIdleTimeMillis(60000 * 60);
// 每十分钟验证一下连接
dataSource.setTimeBetweenEvictionRunsMillis(600000);
// 运行ilde链接测试线程,剔除不可用的链接
dataSource.setTestWhileIdle(true);
dataSource.setMaxWait(5000);
return dataSource;
}
- 使用时先设置当前需要用到的数据源的key值,然后执行sql就可以了
public List
问题
ConcurrentHashMap随着使用增多而导致内存、线程等开销增大,有没有固定容量的map作为容器?
答案是有的,LinkedHashMap就可以控制
public class DynamicDataSourceContainer extends LinkedHashMap
//private Map
这样又会产生新的问题,就是主数据源会被移除,那么如何保证主数据源不被移除呢?
public class DynamicDataSourceContainer extends LinkedHashMap {
//主数据源的临时变量
private static volatile Object tempDs;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
//eldest 为首个元素
final int maxSize = 50;
boolean b = size() > maxSize;
if (b) {
if (DataSourceTypeConstant.mainDB.equals(eldest.getKey())) {
//先缓存下来
tempDs = super.get(DataSourceTypeConstant.mainDB);
//移除,让出容器大小
super.remove(DataSourceTypeConstant.mainDB);
//重新添加到末尾
super.put(DataSourceTypeConstant.mainDB, tempDs);
//不移除首个元素
return false;
} else {
DruidDataSource dataSource = (DruidDataSource) eldest.getValue();
dataSource.close();
}
}
return b;
}
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)