add
This commit is contained in:
@ -2,12 +2,12 @@ package com.qiaoba.common.database.config;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.pool.DruidPooledConnection;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.base.enums.BaseEnum;
|
||||
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
import com.qiaoba.common.database.properties.DataSourceProperties;
|
||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||
@ -18,6 +18,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.annotation.Resource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -83,13 +84,13 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
|
||||
public static DruidDataSource getPrimaryDataSource(String tenantId) {
|
||||
return (DruidDataSource) PRIMARY_DATASOURCE_MAP.get(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
|
||||
return (DruidDataSource) PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||
}
|
||||
|
||||
private void initDefault() {
|
||||
List<DynamicDataSource> dataSources = dataSourceProperties.getDataSources();
|
||||
// 0索引作为主数据源
|
||||
addPrimaryMap(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID, buildDataSource(TenantConstant.DEFAULT_TENANT_ID, dataSources.get(0)));
|
||||
addPrimaryMap(TenantConstant.DEFAULT_TENANT_ID, buildDataSource(TenantConstant.DEFAULT_TENANT_ID, dataSources.get(0)));
|
||||
dataSources.remove(0);
|
||||
// 非0索引的备用
|
||||
addBackupMap(TenantConstant.DEFAULT_TENANT_ID, dataSources);
|
||||
@ -105,7 +106,7 @@ public class DynamicDataSourceConfig {
|
||||
for (int i = 0; i < dataSources.size(); i++) {
|
||||
DynamicDataSource dynamicDataSource = dataSources.get(i);
|
||||
if (BaseEnum.YES.getCode().equals(dynamicDataSource.getIsPrimary())) {
|
||||
addPrimaryMap(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, buildDataSource(dataSources.get(i).getTenantId(), dataSources.get(i)));
|
||||
addPrimaryMap(tenantId, buildDataSource(dataSources.get(i).getTenantId(), dataSources.get(i)));
|
||||
// 去除主要数据源,剩下皆为备用数据源
|
||||
dataSources.remove(i);
|
||||
break;
|
||||
@ -122,12 +123,12 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
|
||||
public void deleteTenantDataSource(String tenantId) {
|
||||
PRIMARY_DATASOURCE_MAP.remove(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
|
||||
PRIMARY_DATASOURCE_MAP.remove(tenantId);
|
||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
}
|
||||
|
||||
public void changePrimaryDatasource(String tenantId, Object datasource) {
|
||||
PRIMARY_DATASOURCE_MAP.put(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, datasource);
|
||||
PRIMARY_DATASOURCE_MAP.put(tenantId, datasource);
|
||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
}
|
||||
|
||||
@ -150,14 +151,9 @@ public class DynamicDataSourceConfig {
|
||||
try {
|
||||
// 初始化数据源
|
||||
dataSource.init();
|
||||
// 将数据源的类型保存
|
||||
DruidPooledConnection connection = dataSource.getConnection();
|
||||
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
// 归还 connection
|
||||
connection.close();
|
||||
return dataSource;
|
||||
} catch (Exception e) {
|
||||
dataSource.close();
|
||||
IoUtil.close(dataSource);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -170,15 +166,22 @@ public class DynamicDataSourceConfig {
|
||||
Set<Map.Entry<Object, Object>> entries = PRIMARY_DATASOURCE_MAP.entrySet();
|
||||
for (Map.Entry<Object, Object> entry : entries) {
|
||||
DruidDataSource dataSource = (DruidDataSource) entry.getValue();
|
||||
if (!dataSource.isClosed()) {
|
||||
dataSource.close();
|
||||
}
|
||||
IoUtil.close(dataSource);
|
||||
}
|
||||
}
|
||||
|
||||
private void addPrimaryMap(String name, Object dataSource) {
|
||||
private void addPrimaryMap(String tenantId, Object dataSource) {
|
||||
if (Objects.nonNull(dataSource)) {
|
||||
PRIMARY_DATASOURCE_MAP.put(name, dataSource);
|
||||
try {
|
||||
PRIMARY_DATASOURCE_MAP.put(tenantId, dataSource);
|
||||
// 将数据源的类型保存
|
||||
DruidPooledConnection connection = ((DruidDataSource) dataSource).getConnection();
|
||||
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
// 归还 connection
|
||||
IoUtil.close(connection);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +197,7 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
|
||||
public void setDefaultSetting() {
|
||||
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDataSource(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setTenantId(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDatabaseType(TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID));
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.qiaoba.common.database.config;
|
||||
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
||||
|
||||
import java.util.Map;
|
||||
@ -22,7 +21,7 @@ public class DynamicDataSourceContext extends AbstractRoutingDataSource {
|
||||
*/
|
||||
public void freshDataSource(Map<Object, Object> targetDataSources) {
|
||||
//默认数据源 (determineCurrentLookupKey 如果找不到就使用默认数据源)
|
||||
super.setDefaultTargetDataSource(targetDataSources.get(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID));
|
||||
super.setDefaultTargetDataSource(targetDataSources.get(TenantConstant.DEFAULT_TENANT_ID));
|
||||
//设置全部数据源
|
||||
super.setTargetDataSources(targetDataSources);
|
||||
//刷新(即把targetDataSources刷到resolvedDataSources中去,resolvedDataSources才是我们真正存放数据源的map)
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.qiaoba.common.database.constants;
|
||||
|
||||
/**
|
||||
* DynamicDatasourceConstant
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023-04-27 19:35:28
|
||||
*/
|
||||
public class DynamicDatasourceConstant {
|
||||
|
||||
/**
|
||||
* 主数据源拼接前缀 master_tenantId
|
||||
*/
|
||||
public static final String MASTER_PREFIX = "master_";
|
||||
|
||||
/**
|
||||
* 从数据源拼接前缀 slave_tenantId_序号
|
||||
*/
|
||||
public static final String SLAVE_PREFIX = "slave_";
|
||||
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package com.qiaoba.common.database.monitor;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -17,6 +16,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 监控-数据源连接监控
|
||||
@ -36,6 +36,8 @@ public class DatasourceConnectionMonitor {
|
||||
@Resource
|
||||
private DynamicDatasourceService dynamicDatasourceService;
|
||||
|
||||
private static Map<String, String> WAIT_UPDATE_DATASOURCE_STATUS = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
new Timer().schedule(new TimerTask() {
|
||||
@ -45,9 +47,9 @@ public class DatasourceConnectionMonitor {
|
||||
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
|
||||
return;
|
||||
}
|
||||
log.debug("开始运行数据源监控线程, 时间: {}", new Date());
|
||||
log.trace("开始运行数据源监控线程, 时间: {}", new Date());
|
||||
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
|
||||
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
|
||||
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||
if (Objects.isNull(primary)) {
|
||||
// 说明初始化主要数据源的时候出错
|
||||
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||
@ -59,17 +61,17 @@ public class DatasourceConnectionMonitor {
|
||||
Connection connection = dataSource.getConnection();
|
||||
if (check(connection)) {
|
||||
// 说明数据源正常
|
||||
log.debug("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
|
||||
connection.close();
|
||||
log.trace("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
|
||||
IoUtil.close(connection);
|
||||
continue;
|
||||
}
|
||||
|
||||
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||
connection.close();
|
||||
IoUtil.close(connection);
|
||||
// 主数据源异常 切换备用数据源
|
||||
if (changePrimary(tenantId)) {
|
||||
// 备用切换成功, 关闭原有异常数据源
|
||||
dataSource.close();
|
||||
IoUtil.close(dataSource);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
@ -77,7 +79,7 @@ public class DatasourceConnectionMonitor {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("结束运行数据源监控线程, 时间: {}", new Date());
|
||||
log.trace("结束运行数据源监控线程, 时间: {}", new Date());
|
||||
}
|
||||
}, 0, 1000);
|
||||
}
|
||||
@ -93,8 +95,8 @@ public class DatasourceConnectionMonitor {
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} finally {
|
||||
JdbcUtils.close(rs);
|
||||
JdbcUtils.close(stmt);
|
||||
IoUtil.close(rs);
|
||||
IoUtil.close(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +130,12 @@ public class DatasourceConnectionMonitor {
|
||||
DynamicDataSource dynamicDataSource = dataSources.get(backupIndex);
|
||||
// 更改数据库中该数据源为主要数据源
|
||||
if (!TenantConstant.DEFAULT_TENANT_ID.equals(dynamicDataSource.getTenantId())) {
|
||||
dynamicDatasourceService.changePrimaryDatasource(dynamicDataSource.getTenantId(), dynamicDataSource.getDatasourceId());
|
||||
try {
|
||||
dynamicDatasourceService.changePrimaryDatasource(dynamicDataSource.getTenantId(), dynamicDataSource.getDatasourceId());
|
||||
} catch (Exception e) {
|
||||
// 说明主数据源也挂了
|
||||
WAIT_UPDATE_DATASOURCE_STATUS.put(dynamicDataSource.getTenantId(), dynamicDataSource.getDatasourceId());
|
||||
}
|
||||
}
|
||||
// 备用数据源集合删除该数据源
|
||||
dataSources.remove((int) backupIndex);
|
||||
|
@ -3,6 +3,7 @@ package com.qiaoba.common.database.utils;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.qiaoba.common.base.exceptions.ServiceException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
@ -14,6 +15,7 @@ import java.sql.DriverManager;
|
||||
* @version 1.0
|
||||
* @since 2021/10/15 0015 下午 16:43
|
||||
*/
|
||||
@Slf4j
|
||||
public class JdbcUtil {
|
||||
|
||||
/**
|
||||
@ -33,7 +35,7 @@ public class JdbcUtil {
|
||||
conn = DriverManager.getConnection(url, username, password);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.debug(e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
IoUtil.close(conn);
|
||||
|
@ -3,11 +3,9 @@ package com.qiaoba.module.tenant.filters;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.qiaoba.common.base.code.TenantErrorCode;
|
||||
import com.qiaoba.common.base.constants.BaseConstant;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
||||
import com.qiaoba.common.web.utils.ResponseUtil;
|
||||
import com.qiaoba.common.web.utils.UriUtil;
|
||||
import com.qiaoba.module.tenant.entity.SysTenant;
|
||||
@ -86,12 +84,12 @@ public class DynamicDataSourceFilter extends OncePerRequestFilter {
|
||||
// 数据源模式-设置第三方数据源
|
||||
if (sysTenant.getMode().equals(SysTenant.DATASOURCE_MODE)) {
|
||||
//设置当前租户对应的数据源
|
||||
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + sysTenant.getTenantId());
|
||||
BaseContext.setDataSource(sysTenant.getTenantId());
|
||||
BaseContext.setDatabaseType(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(sysTenant.getTenantId()));
|
||||
}
|
||||
// 字段模式 or Schema模式-数据源选择默认数据源
|
||||
else {
|
||||
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDataSource(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDatabaseType(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user