This commit is contained in:
2023-06-15 17:46:38 +08:00
parent 076955a04f
commit fc533fa2e8
6 changed files with 46 additions and 59 deletions

View File

@ -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));
}

View File

@ -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)

View File

@ -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_";
}

View File

@ -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);

View File

@ -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);

View File

@ -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));
}