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