package com.taobao.tddl.atom.config;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.taobao.tddl.atom.TAtomDbStatusEnum;
import com.taobao.tddl.atom.TAtomDbTypeEnum;
import com.taobao.tddl.atom.common.TAtomConURLTools;
import com.taobao.tddl.atom.common.TAtomConstants;
import com.taobao.tddl.atom.config.listener.AtomDbStatusListener;
import com.taobao.tddl.atom.exception.AtomAlreadyInitException;
import com.taobao.tddl.atom.exception.AtomIllegalException;
import com.taobao.tddl.atom.exception.AtomInitialException;
import com.taobao.tddl.atom.jdbc.TDataSourceWrapper;
import com.taobao.tddl.atom.utils.ConnRestrictEntry;
import com.taobao.tddl.common.utils.TStringUtil;
import com.taobao.tddl.config.ConfigDataListener;
import com.taobao.tddl.monitor.Monitor;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* 数据库动态切换的Handle类,所有数据库的动态切换 都是由这个类完成
*
* @author qihao
*/
public class TAtomDsConfHandle {
private static Logger logger = LoggerFactory.getLogger(TAtomDsConfHandle.class);
private String appName;
private String dbKey;
private String unitName;
/**
* 运行时配置
*/
private volatile TAtomDsConfDO runTimeConf = new TAtomDsConfDO();
/**
* 本地配置,优先于推送的动态配置
*/
private TAtomDsConfDO localConf = new TAtomDsConfDO();
/**
* 全局配置,应用配置订阅管理
*/
private DbConfManager dbConfManager;
/**
* 密码配置订阅管理
*/
private DbPasswdManager dbPasswdManager;
/**
* druid数据源通过init初始化
*/
private volatile DruidDataSource druidDataSource;
/**
* 数据库状态改变回调
*/
private volatile List<AtomDbStatusListener> dbStatusListeners;
/**
* 初始化标记为一但初始化过,所有本地的配置禁止改动
*/
private volatile boolean initFalg;
/**
* 数据源操作锁,当需要对数据源进行重建或者刷新时需要先获得该锁
*/
private final ReentrantLock lock = new ReentrantLock();
// public static final int druidStatMaxKeySize = 5000;
// public static final int druidFlushIntervalMill = 300*1000;
/**
* 初始化方法,创建对应的数据源,只能被调用一次
*
* @throws Exception
*/
public void init() throws Exception {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] double call Init !");
}
// 1.初始化参数检查
if (TStringUtil.isBlank(this.appName) || TStringUtil.isBlank(this.dbKey)) {
String errorMsg = "[attributeError] TAtomDatasource of appName Or dbKey is Empty !";
logger.error(errorMsg);
throw new AtomIllegalException(errorMsg);
}
// 2.配置dbConfManager
AtomConfigManager defaultDbConfManager = new AtomConfigManager();
defaultDbConfManager.setGlobalConfigDataId(TAtomConstants.getGlobalDataId(this.dbKey));
defaultDbConfManager.setAppConfigDataId(TAtomConstants.getAppDataId(this.appName, this.dbKey));
defaultDbConfManager.setUnitName(unitName);
// 初始化dbConfManager
defaultDbConfManager.init(appName);
dbConfManager = defaultDbConfManager;
// 3.获取全局配置
String globaConfStr = dbConfManager.getGlobalDbConf();
// 注册全局配置监听
registerGlobaDbConfListener(defaultDbConfManager);
if (TStringUtil.isBlank(globaConfStr)) {
String errorMsg = "[ConfError] read globalConfig is Empty !";
logger.error(errorMsg);
throw new AtomInitialException(errorMsg);
}
// 4.获取应用配置
String appConfStr = dbConfManager.getAppDbDbConf();
// 注册应用配置监听
registerAppDbConfListener(defaultDbConfManager);
if (TStringUtil.isBlank(appConfStr)) {
String errorMsg = "[ConfError] read appConfig is Empty !";
logger.error(errorMsg);
throw new AtomInitialException(errorMsg);
}
lock.lock();
try {
// 5.解析配置string成TAtomDsConfDO
runTimeConf = TAtomConfParser.parserTAtomDsConfDO(globaConfStr, appConfStr);
// 6.处理本地优先配置
overConfByLocal(localConf, runTimeConf);
// 7.如果没有设置本地密码,则用订的密码,初始化passwdManager
if (TStringUtil.isBlank(this.runTimeConf.getPasswd())) {
// 检查dbKey和对应的userName是否为空
if (TStringUtil.isBlank(runTimeConf.getUserName())) {
String errorMsg = "[attributeError] TAtomDatasource of UserName is Empty !";
logger.error(errorMsg);
throw new AtomIllegalException(errorMsg);
}
AtomPasswdManager diamondDbPasswdManager = new AtomPasswdManager();
diamondDbPasswdManager.setPasswdConfDataId(TAtomConstants.getPasswdDataId(runTimeConf.getDbName(),
runTimeConf.getDbType(),
runTimeConf.getUserName()));
diamondDbPasswdManager.setUnitName(unitName);
diamondDbPasswdManager.init(appName);
dbPasswdManager = diamondDbPasswdManager;
// 获取密码
String passwd = dbPasswdManager.getPasswd();
registerPasswdConfListener(diamondDbPasswdManager);
if (TStringUtil.isBlank(passwd)) {
String errorMsg = "[PasswdError] read passwd is Empty !";
logger.error(errorMsg);
throw new AtomInitialException(errorMsg);
}
runTimeConf.setPasswd(passwd);
}
// 8.转换tAtomDsConfDO
DruidDataSource druidDataSource = convertTAtomDsConf2DruidConf(TAtomDsConfHandle.this.dbKey,
this.runTimeConf,
TAtomConstants.getDbNameStr(this.unitName, this.appName, this.dbKey));
// 9.参数检查如果参数不正确直接抛出异常
if (!checkLocalTxDataSourceDO(druidDataSource)) {
String errorMsg = "[ConfigError]init dataSource Prams Error! config is : " + druidDataSource.toString();
logger.error(errorMsg);
throw new AtomInitialException(errorMsg);
}
// 10.创建数据源
// druidDataSource.setUseJmx(false);
// LocalTxDataSource localTxDataSource = TaobaoDataSourceFactory
// .createLocalTxDataSource(localTxDataSourceDO);
// 11.将创建好的数据源是指到TAtomDatasource中
druidDataSource.init();
// druidDataSource.getDataSourceStat().setMaxSqlSize(DruidDsConfHandle.druidStatMaxKeySize);
this.druidDataSource = druidDataSource;
clearDataSourceWrapper();
initFalg = true;
} finally {
lock.unlock();
}
}
private void clearDataSourceWrapper() {
Monitor.removeSnapshotValuesCallback(wrapDataSource);
wrapDataSource = null;
}
/**
* 注册密码变化监听器
*
* @param dbPasswdManager
*/
private void registerPasswdConfListener(DbPasswdManager dbPasswdManager) {
dbPasswdManager.registerPasswdConfListener(new ConfigDataListener() {
public void onDataRecieved(String dataId, String data) {
logger.warn("[Passwd HandleData] dataId : " + dataId + " data: " + data);
if (null == data || TStringUtil.isBlank(data)) {
return;
}
lock.lock();
try {
String localPasswd = TAtomDsConfHandle.this.localConf.getPasswd();
if (TStringUtil.isNotBlank(localPasswd)) {
// 如果本地配置了passwd直接返回不支持动态修改
return;
}
String newPasswd = TAtomConfParser.parserPasswd(data);
String runPasswd = TAtomDsConfHandle.this.runTimeConf.getPasswd();
if (!TStringUtil.equals(runPasswd, newPasswd)) {
try {
// modify by junyu 2013-06-14:dynamic change
// passwd,not recreate it!
// DruidDataSource newDruidDataSource =
// DruidDsConfHandle.this.druidDataSource.cloneDruidDataSource();
// newDruidDataSource.setPassword(newPasswd);
// newDruidDataSource.init();
// DruidDataSource tempDataSource =
// DruidDsConfHandle.this.druidDataSource;
// DruidDsConfHandle.this.druidDataSource =
// newDruidDataSource;
// tempDataSource.close();
// logger.warn("[DRUID CHANGE PASSWORD] ReCreate DataSource !");
// 是用新的配置覆盖运行时的配置
// clearDataSourceWrapper();
TAtomDsConfHandle.this.druidDataSource.setPassword(newPasswd);
logger.warn("[DRUID CHANGE PASSWORD] already reset the new passwd!");
TAtomDsConfHandle.this.runTimeConf.setPasswd(newPasswd);
} catch (Exception e) {
logger.error("[DRUID CHANGE PASSWORD] reset new passwd error!", e);
}
}
} finally {
lock.unlock();
}
}
});
}
/**
* 全局配置监听,全局配置发生变化, 需要重新FLUSH数据源
*
* @param defaultDbConfManager
*/
private void registerGlobaDbConfListener(DbConfManager dbConfManager) {
dbConfManager.registerGlobaDbConfListener(new ConfigDataListener() {
public void onDataRecieved(String dataId, String data) {
logger.warn("[DRUID GlobaConf HandleData] dataId : " + dataId + " data: " + data);
if (null == data || TStringUtil.isBlank(data)) {
return;
}
lock.lock();
try {
String globaConfStr = data;
// 如果是全局配置发生变化,可能是IP,PORT,DBNAME,DBTYPE,STATUS
TAtomDsConfDO tmpConf = TAtomConfParser.parserTAtomDsConfDO(globaConfStr, null);
TAtomDsConfDO newConf = TAtomDsConfHandle.this.runTimeConf.clone();
// 是用推送的配置,覆盖当前的配置
newConf.setIp(tmpConf.getIp());
newConf.setPort(tmpConf.getPort());
newConf.setDbName(tmpConf.getDbName());
newConf.setDbType(tmpConf.getDbType());
newConf.setDbStatus(tmpConf.getDbStatus());
// 处理本地优先配置
overConfByLocal(TAtomDsConfHandle.this.localConf, newConf);
// 如果推送过来的数据库状态是 RW/R->NA,直接销毁掉数据源,以下业务逻辑不做处理
if (TAtomDbStatusEnum.NA_STATUS != TAtomDsConfHandle.this.runTimeConf.getDbStautsEnum()
&& TAtomDbStatusEnum.NA_STATUS == tmpConf.getDbStautsEnum()) {
try {
TAtomDsConfHandle.this.druidDataSource.close();
logger.warn("[DRUID NA STATUS PUSH] destroy DataSource !");
} catch (Exception e) {
logger.error("[DRUID NA STATUS PUSH] destroy DataSource Error!", e);
}
} else {
// 转换tAtomDsConfDO
DruidDataSource druidDataSource;
try {
druidDataSource = convertTAtomDsConf2DruidConf(TAtomDsConfHandle.this.dbKey,
newConf,
TAtomConstants.getDbNameStr(TAtomDsConfHandle.this.unitName,
TAtomDsConfHandle.this.appName,
TAtomDsConfHandle.this.dbKey));
} catch (Exception e1) {
logger.error("[DRUID GlobaConfError] convertTAtomDsConf2DruidConf Error! dataId : "
+ dataId + " config : " + data);
return;
}
// 检查转换后结果是否正确
if (!checkLocalTxDataSourceDO(druidDataSource)) {
logger.error("[DRUID GlobaConfError] dataSource Prams Error! dataId : " + dataId
+ " config : " + data);
return;
}
// 如果推送的状态时 NA->RW/R 时需要重新创建数据源,无需再刷新
if (TAtomDsConfHandle.this.runTimeConf.getDbStautsEnum() == TAtomDbStatusEnum.NA_STATUS
&& (newConf.getDbStautsEnum() == TAtomDbStatusEnum.RW_STATUS
|| newConf.getDbStautsEnum() == TAtomDbStatusEnum.R_STATUS || newConf.getDbStautsEnum() == TAtomDbStatusEnum.W_STATUS)) {
// 创建数据源
try {
// 关闭TB-DATASOURCE的JMX注册
// localTxDataSourceDO.setUseJmx(false);
// LocalTxDataSource localTxDataSource =
// TaobaoDataSourceFactory
// .createLocalTxDataSource(localTxDataSourceDO);
druidDataSource.init();
// druidDataSource.getDataSourceStat().setMaxSqlSize(DruidDsConfHandle.druidStatMaxKeySize);
DruidDataSource tempDataSource = TAtomDsConfHandle.this.druidDataSource;
TAtomDsConfHandle.this.druidDataSource = druidDataSource;
tempDataSource.close();
logger.warn("[DRUID NA->RW/R STATUS PUSH] ReCreate DataSource !");
} catch (Exception e) {
logger.error("[DRUID NA->RW/R STATUS PUSH] ReCreate DataSource Error!", e);
}
} else {
boolean needCreate = isGlobalChangeNeedReCreate(TAtomDsConfHandle.this.runTimeConf, newConf);
// 如果发生的配置变化是否需要重建数据源
// druid 没有flush方法,只能重建数据源 jiechen.qzm
if (needCreate) {
try {
// 更新数据源
druidDataSource.init();
// druidDataSource.getDataSourceStat().setMaxSqlSize(druidStatMaxKeySize);
DruidDataSource tempDataSource = TAtomDsConfHandle.this.druidDataSource;
TAtomDsConfHandle.this.druidDataSource = druidDataSource;
tempDataSource.close();
logger.warn("[DRUID CONFIG CHANGE STATUS] Always ReCreate DataSource !");
} catch (Exception e) {
logger.error("[DRUID Create GlobaConf Error] Always ReCreate DataSource Error !",
e);
}
} else {
logger.warn("[DRUID Create GlobaConf Error] global config is same!nothing will be done! the global config is:"
+ globaConfStr);
}
}
}
// 处理数据库状态监听器
processDbStatusListener(TAtomDsConfHandle.this.runTimeConf.getDbStautsEnum(),
newConf.getDbStautsEnum());
// 是用新的配置覆盖运行时的配置
TAtomDsConfHandle.this.runTimeConf = newConf;
clearDataSourceWrapper();
} finally {
lock.unlock();
}
}
private boolean isGlobalChangeNeedReCreate(TAtomDsConfDO runConf, TAtomDsConfDO newConf) {
boolean needReCreate = false;
if (!TStringUtil.equals(runConf.getIp(), newConf.getIp())) {
needReCreate = true;
return needReCreate;
}
if (!TStringUtil.equals(runConf.getPort(), newConf.getPort())) {
needReCreate = true;
return needReCreate;
}
if (!TStringUtil.equals(runConf.getDbName(), newConf.getDbName())) {
needReCreate = true;
return needReCreate;
}
if (runConf.getDbTypeEnum() != newConf.getDbTypeEnum()) {
needReCreate = true;
return needReCreate;
}
return needReCreate;
}
});
}
/**
* 应用配置监听,当应用配置发生变化时,区分发生 变化的配置,来决定具体是flush还是reCreate
*
* @param defaultDbConfManager
*/
private void registerAppDbConfListener(DbConfManager dbConfManager) {
dbConfManager.registerAppDbConfListener(new ConfigDataListener() {
public void onDataRecieved(String dataId, String data) {
logger.warn("[DRUID AppConf HandleData] dataId : " + dataId + " data: " + data);
if (null == data || TStringUtil.isBlank(data)) {
return;
}
lock.lock();
try {
String appConfStr = data;
TAtomDsConfDO tmpConf = TAtomConfParser.parserTAtomDsConfDO(null, appConfStr);
TAtomDsConfDO newConf = TAtomDsConfHandle.this.runTimeConf.clone();
// 有些既有配置不能变更,所以克隆老的配置,然后将新的set进去
newConf.setUserName(tmpConf.getUserName());
newConf.setMinPoolSize(tmpConf.getMinPoolSize());
newConf.setMaxPoolSize(tmpConf.getMaxPoolSize());
newConf.setInitPoolSize(tmpConf.getInitPoolSize());
newConf.setIdleTimeout(tmpConf.getIdleTimeout());
newConf.setBlockingTimeout(tmpConf.getBlockingTimeout());
newConf.setPreparedStatementCacheSize(tmpConf.getPreparedStatementCacheSize());
newConf.setConnectionProperties(tmpConf.getConnectionProperties());
newConf.setOracleConType(tmpConf.getOracleConType());
// 增加3个具体的实现
newConf.setWriteRestrictTimes(tmpConf.getWriteRestrictTimes());
newConf.setReadRestrictTimes(tmpConf.getReadRestrictTimes());
newConf.setThreadCountRestrict(tmpConf.getThreadCountRestrict());
newConf.setTimeSliceInMillis(tmpConf.getTimeSliceInMillis());
newConf.setDriverClass(tmpConf.getDriverClass());
// 处理本地优先配置
overConfByLocal(TAtomDsConfHandle.this.localConf, newConf);
boolean isNeedReCreate = isAppChangeNeedReCreate(TAtomDsConfHandle.this.runTimeConf, newConf);
if (isNeedReCreate) {
// 转换tAtomDsConfDO
DruidDataSource druidDataSource;
try {
druidDataSource = convertTAtomDsConf2DruidConf(TAtomDsConfHandle.this.dbKey,
newConf,
TAtomConstants.getDbNameStr(TAtomDsConfHandle.this.unitName,
TAtomDsConfHandle.this.appName,
TAtomDsConfHandle.this.dbKey));
} catch (Exception e1) {
logger.error("[DRUID GlobaConfError] convertTAtomDsConf2DruidConf Error! dataId : "
+ dataId + " config : " + data);
return;
}
// 检查转换后结果是否正确
if (!checkLocalTxDataSourceDO(druidDataSource)) {
logger.error("[DRUID GlobaConfError] dataSource Prams Error! dataId : " + dataId
+ " config : " + data);
return;
}
try {
// 这个必须在最前面,否则下次推送可能无法比较出不同而不创建新数据
TAtomDsConfHandle.this.runTimeConf = newConf;
TAtomDsConfHandle.this.druidDataSource.close();
logger.warn("[DRUID destroy OldDataSource] dataId : " + dataId);
druidDataSource.init();
// druidDataSource.getDataSourceStat().setMaxSqlSize(DruidDsConfHandle.druidStatMaxKeySize);
logger.warn("[DRUID create newDataSource] dataId : " + dataId);
TAtomDsConfHandle.this.druidDataSource = druidDataSource;
clearDataSourceWrapper();
} catch (Exception e) {
logger.error("[DRUID Create GlobaConf Error] Always ReCreate DataSource Error ! dataId: "
+ dataId, e);
}
} else {
boolean isNeedFlush = isAppChangeNeedFlush(TAtomDsConfHandle.this.runTimeConf, newConf);
/**
* 阀值变化无需刷新持有的数据源,只要更新runTimeConf,并且清空wrapDataSource
*/
boolean isRestrictChange = isRestrictChange(TAtomDsConfHandle.this.runTimeConf, newConf);
if (isNeedFlush) {
try {
TAtomDsConfHandle.this.runTimeConf = newConf;
Properties prop = new Properties();
prop.putAll(newConf.getConnectionProperties());
TAtomDsConfHandle.this.druidDataSource.setConnectProperties(prop);
TAtomDsConfHandle.this.druidDataSource.setMinIdle(newConf.getMinPoolSize());
TAtomDsConfHandle.this.druidDataSource.setMaxActive(newConf.getMaxPoolSize());
if (newConf.getPreparedStatementCacheSize() > 0
&& TAtomDbTypeEnum.MYSQL != newConf.getDbTypeEnum()) {
TAtomDsConfHandle.this.druidDataSource.setPoolPreparedStatements(true);
TAtomDsConfHandle.this.druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(newConf.getPreparedStatementCacheSize());
}
if (newConf.getIdleTimeout() > 0) {
TAtomDsConfHandle.this.druidDataSource.setTimeBetweenEvictionRunsMillis(newConf.getIdleTimeout() * 60 * 1000);
TAtomDsConfHandle.this.druidDataSource.setMinEvictableIdleTimeMillis(newConf.getIdleTimeout() * 60 * 1000);
}
if (newConf.getBlockingTimeout() > 0) {
TAtomDsConfHandle.this.druidDataSource.setMaxWait(newConf.getBlockingTimeout());
}
logger.info("[TDDL DRUID] flush ds success,dataId : " + dataId);
clearDataSourceWrapper();
} catch (Exception e) {
logger.error("[TDDL DRUID] flush DataSource Error ! dataId:" + dataId + ",data:"
+ appConfStr, e);
}
} else if (isRestrictChange) {
TAtomDsConfHandle.this.runTimeConf = newConf;
clearDataSourceWrapper();
}
}
} finally {
lock.unlock();
}
}
private boolean isAppChangeNeedReCreate(TAtomDsConfDO runConf, TAtomDsConfDO newConf) {
if (!newConf.getDriverClass().equals(runConf.getDriverClass())) {
return true;
}
if (TAtomDbTypeEnum.ORACLE == newConf.getDbTypeEnum()) {
Map<String, String> newProp = newConf.getConnectionProperties();
Map<String, String> runProp = runConf.getConnectionProperties();
// oracle的连接参数变化会导致
if (!runProp.equals(newProp)) {
return true;
}
}
if (!TStringUtil.equals(runConf.getUserName(), newConf.getUserName())) {
return true;
}
if (runConf.getOracleConType() != newConf.getOracleConType()) {
return true;
}
if (runConf.getDbTypeEnum() != newConf.getDbTypeEnum()) {
return true;
}
return false;
}
private boolean isAppChangeNeedFlush(TAtomDsConfDO runConf, TAtomDsConfDO newConf) {
if (TAtomDbTypeEnum.MYSQL == newConf.getDbTypeEnum()) {
Map<String, String> newProp = newConf.getConnectionProperties();
Map<String, String> runProp = runConf.getConnectionProperties();
if (!runProp.equals(newProp)) {
return true;
}
}
if (!TStringUtil.equals(runConf.getPasswd(), newConf.getPasswd())) {
return true;
}
if (runConf.getMinPoolSize() != newConf.getMinPoolSize()) {
return true;
}
if (runConf.getMaxPoolSize() != newConf.getMaxPoolSize()) {
return true;
}
if (runConf.getIdleTimeout() != newConf.getIdleTimeout()) {
return true;
}
if (runConf.getBlockingTimeout() != newConf.getBlockingTimeout()) {
return true;
}
if (runConf.getPreparedStatementCacheSize() != newConf.getPreparedStatementCacheSize()) {
return true;
}
return false;
}
private boolean isRestrictChange(TAtomDsConfDO runConf, TAtomDsConfDO newConf) {
if (runConf.getReadRestrictTimes() != newConf.getReadRestrictTimes()) {
return true;
}
if (runConf.getWriteRestrictTimes() != newConf.getWriteRestrictTimes()) {
return true;
}
if (runConf.getThreadCountRestrict() != newConf.getThreadCountRestrict()) {
return true;
}
if (runConf.getTimeSliceInMillis() != newConf.getTimeSliceInMillis()) {
return true;
}
List<ConnRestrictEntry> runEntries = runConf.getConnRestrictEntries();
List<ConnRestrictEntry> newEntries = newConf.getConnRestrictEntries();
if (runEntries != newEntries) {
if (runEntries == null || newEntries == null || !newEntries.equals(runEntries)) {
return true;
}
}
return false;
}
});
}
/**
* druid特殊开关,针对特殊编码转换,目前是B2B中文站专用
*
* @param connectionProperties
* @param druidDataSource
* @throws SQLException
*/
public static void fillDruidFilters(Map<String, String> connectionProperties, DruidDataSource druidDataSource)
throws SQLException {
if (connectionProperties.containsKey("clientEncoding") || connectionProperties.containsKey("serverEncoding")) {
druidDataSource.setFilters(TDDL_DRUID_ENCODING_FILTER);
}
}
private static final String TDDL_DRUID_ENCODING_FILTER = "encoding";
// private static final String DEFAULT_TDDL_DRUID_FILTERS="mergeStat";
/**
* 将TAtomDsConfDO转换成LocalTxDataSourceDO
*
* @param tAtomDsConfDO
* @return
*/
@SuppressWarnings("rawtypes")
public static DruidDataSource convertTAtomDsConf2DruidConf(String dbKey, TAtomDsConfDO tAtomDsConfDO, String dbName)
throws Exception {
DruidDataSource localDruidDataSource = new DruidDataSource();
// 一下三个是druid监控需要的特殊配置
localDruidDataSource.setName(dbKey);
localDruidDataSource.setTestOnBorrow(false);
localDruidDataSource.setTestWhileIdle(true);
// localDruidDataSource.setFilters(DEFAULT_TDDL_DRUID_FILTERS);
localDruidDataSource.setUsername(tAtomDsConfDO.getUserName());
localDruidDataSource.setPassword(tAtomDsConfDO.getPasswd());
localDruidDataSource.setDriverClassName(tAtomDsConfDO.getDriverClass());
localDruidDataSource.setExceptionSorterClassName(tAtomDsConfDO.getSorterClass());
// 根据数据库类型设置conURL和setConnectionProperties
if (TAtomDbTypeEnum.ORACLE == tAtomDsConfDO.getDbTypeEnum()) {
String conUlr = TAtomConURLTools.getOracleConURL(tAtomDsConfDO.getIp(),
tAtomDsConfDO.getPort(),
tAtomDsConfDO.getDbName(),
tAtomDsConfDO.getOracleConType());
localDruidDataSource.setUrl(conUlr);
// 如果是oracle没有设置ConnectionProperties则给以个默认的
Properties connectionProperties = new Properties();
if (!tAtomDsConfDO.getConnectionProperties().isEmpty()) {
connectionProperties.putAll(tAtomDsConfDO.getConnectionProperties());
fillDruidFilters(tAtomDsConfDO.getConnectionProperties(), localDruidDataSource);
} else {
connectionProperties.putAll(TAtomConstants.DEFAULT_ORACLE_CONNECTION_PROPERTIES);
}
localDruidDataSource.setConnectProperties(connectionProperties);
localDruidDataSource.setValidationQuery(TAtomConstants.DEFAULT_DRUID_ORACLE_VALIDATION_QUERY);
} else if (TAtomDbTypeEnum.MYSQL == tAtomDsConfDO.getDbTypeEnum()) {
String conUlr = TAtomConURLTools.getMySqlConURL(tAtomDsConfDO.getIp(),
tAtomDsConfDO.getPort(),
tAtomDsConfDO.getDbName(),
tAtomDsConfDO.getConnectionProperties());
localDruidDataSource.setUrl(conUlr);
// 如果可以找到mysqlDriver中的Valid就使用,否则不设置valid
String validConnnectionCheckerClassName = TAtomConstants.DEFAULT_DRUID_MYSQL_VALID_CONNECTION_CHECKERCLASS;
try {
Class.forName(validConnnectionCheckerClassName);
localDruidDataSource.setValidConnectionCheckerClassName(validConnnectionCheckerClassName);
} catch (ClassNotFoundException e) {
logger.warn("MYSQL Driver is Not Suport " + validConnnectionCheckerClassName);
} catch (NoClassDefFoundError e) {
logger.warn("MYSQL Driver is Not Suport " + validConnnectionCheckerClassName);
}
// 如果可以找到mysqlDriver中的integrationSorter就使用否则使用默认的
String integrationSorterCalssName = TAtomConstants.DRUID_MYSQL_INTEGRATION_SORTER_CLASS;
String defaultIntegrationSorterCalssName = TAtomConstants.DEFAULT_DRUID_MYSQL_SORTER_CLASS;
try {
Class integrationSorterCalss = Class.forName(integrationSorterCalssName);
if (null != integrationSorterCalss) {
localDruidDataSource.setExceptionSorterClassName(integrationSorterCalssName);
} else {
localDruidDataSource.setExceptionSorterClassName(defaultIntegrationSorterCalssName);
logger.warn("MYSQL Driver is Not Suport " + integrationSorterCalssName + " use default sorter "
+ defaultIntegrationSorterCalssName);
}
} catch (ClassNotFoundException e) {
logger.warn("MYSQL Driver is Not Suport " + integrationSorterCalssName + " use default sorter "
+ defaultIntegrationSorterCalssName);
localDruidDataSource.setExceptionSorterClassName(defaultIntegrationSorterCalssName);
} catch (NoClassDefFoundError e) {
logger.warn("MYSQL Driver is Not Suport " + integrationSorterCalssName + " use default sorter "
+ defaultIntegrationSorterCalssName);
localDruidDataSource.setExceptionSorterClassName(defaultIntegrationSorterCalssName);
}
localDruidDataSource.setValidationQuery(TAtomConstants.DEFAULT_DRUID_MYSQL_VALIDATION_QUERY);
}
// lazy init 先设置为0 后续真正执行时才创建连接
localDruidDataSource.setInitialSize(tAtomDsConfDO.getInitPoolSize());
localDruidDataSource.setMinIdle(tAtomDsConfDO.getMinPoolSize());
localDruidDataSource.setMaxActive(tAtomDsConfDO.getMaxPoolSize());
if (tAtomDsConfDO.getPreparedStatementCacheSize() > 0 && TAtomDbTypeEnum.MYSQL != tAtomDsConfDO.getDbTypeEnum()) {
localDruidDataSource.setPoolPreparedStatements(true);
localDruidDataSource.setMaxPoolPreparedStatementPerConnectionSize(tAtomDsConfDO.getPreparedStatementCacheSize());
}
if (tAtomDsConfDO.getIdleTimeout() > 0) {
localDruidDataSource.setTimeBetweenEvictionRunsMillis(tAtomDsConfDO.getIdleTimeout() * 60 * 1000);
localDruidDataSource.setMinEvictableIdleTimeMillis(tAtomDsConfDO.getIdleTimeout() * 60 * 1000);
}
if (tAtomDsConfDO.getBlockingTimeout() > 0) {
localDruidDataSource.setMaxWait(tAtomDsConfDO.getBlockingTimeout());
}
// 添加druid日志输出
// DruidDataSourceStatLogger
// logger=localDruidDataSource.getStatLogger();
// logger.setLogger(new Log4jImpl(LoggerInit.TDDL_Atom_Statistic_LOG));
// localDruidDataSource.setTimeBetweenLogStatsMillis(DruidDsConfHandle.druidFlushIntervalMill);
return localDruidDataSource;
}
public static boolean checkLocalTxDataSourceDO(DruidDataSource druidDataSource) {
if (null == druidDataSource) {
return false;
}
if (TStringUtil.isBlank(druidDataSource.getUrl())) {
logger.error("[DsConfig Check] URL is Empty !");
return false;
}
if (TStringUtil.isBlank(druidDataSource.getUsername())) {
logger.error("[DsConfig Check] Username is Empty !");
return false;
}
if (TStringUtil.isBlank(druidDataSource.getPassword())) {
logger.error("[DsConfig Check] Password is Empty !");
return false;
}
if (TStringUtil.isBlank(druidDataSource.getDriverClassName())) {
logger.error("[DsConfig Check] DriverClassName is Empty !");
return false;
}
if (druidDataSource.getMinIdle() < 1) {
logger.error("[DsConfig Check] MinIdle Error size is:" + druidDataSource.getMinIdle());
return false;
}
if (druidDataSource.getMaxActive() < 1) {
logger.error("[DsConfig Check] MaxActive Error size is:" + druidDataSource.getMaxActive());
return false;
}
if (druidDataSource.getMinIdle() > druidDataSource.getMaxActive()) {
logger.error("[DsConfig Check] MinPoolSize Over MaxPoolSize Minsize is:" + druidDataSource.getMinIdle()
+ "MaxSize is :" + druidDataSource.getMaxActive());
return false;
}
return true;
}
/**
* 是用本地配置覆盖传入的TAtomDsConfDO的属性
*
* @param tAtomDsConfDO
*/
private void overConfByLocal(TAtomDsConfDO localDsConfDO, TAtomDsConfDO newDsConfDO) {
if (null == newDsConfDO || null == localDsConfDO) {
return;
}
// 允许设置driverClass
// if (StringUtil.isNotBlank(localDsConfDO.getDriverClass())) {
// newDsConfDO.setDriverClass(localDsConfDO.getDriverClass());
// }
if (TStringUtil.isNotBlank(localDsConfDO.getSorterClass())) {
newDsConfDO.setSorterClass(localDsConfDO.getSorterClass());
}
if (TStringUtil.isNotBlank(localDsConfDO.getPasswd())) {
newDsConfDO.setPasswd(localDsConfDO.getPasswd());
}
if (null != localDsConfDO.getConnectionProperties() && !localDsConfDO.getConnectionProperties().isEmpty()) {
newDsConfDO.setConnectionProperties(localDsConfDO.getConnectionProperties());
}
}
/**
* Datasource 的包装类
*/
private volatile TDataSourceWrapper wrapDataSource = null;
public DataSource getDataSource() throws SQLException {
if (wrapDataSource == null) {
lock.lock();
try {
if (wrapDataSource != null) {
// 双检查锁
return wrapDataSource;
}
String errorMsg = "";
if (null == druidDataSource) {
errorMsg = "[InitError] TAtomDsConfHandle maybe forget init !";
logger.error(errorMsg);
throw new SQLException(errorMsg);
}
DataSource dataSource = druidDataSource;
if (null == dataSource) {
errorMsg = "[InitError] TAtomDsConfHandle maybe init fail !";
logger.error(errorMsg);
throw new SQLException(errorMsg);
}
// 如果数据库状态不可用直接抛出异常
if (null == this.getStatus()) {
errorMsg = "[DB Stats Error] DbStatus is Null: " + this.getDbKey();
logger.error(errorMsg);
throw new SQLException(errorMsg);
}
TDataSourceWrapper tDataSourceWrapper = new TDataSourceWrapper(dataSource, runTimeConf);
tDataSourceWrapper.setDatasourceName(dbKey);
tDataSourceWrapper.setDatasourceIp(runTimeConf.getIp());
tDataSourceWrapper.setDatasourcePort(runTimeConf.getPort());
tDataSourceWrapper.setDatasourceRealDbName(runTimeConf.getDbName());
tDataSourceWrapper.setDbStatus(getStatus());
logger.warn("set datasource key: " + dbKey);
tDataSourceWrapper.init();
wrapDataSource = tDataSourceWrapper;
return wrapDataSource;
} finally {
lock.unlock();
}
} else {
return wrapDataSource;
}
}
public void flushDataSource() {
// 暂时不支持flush 抛错
logger.error("DRUID DATASOURCE DO NOT SUPPORT FLUSH.");
throw new RuntimeException("DRUID DATASOURCE DO NOT SUPPORT FLUSH.");
}
public void destroyDataSource() throws Exception {
if (null != this.druidDataSource) {
logger.warn("[DataSource Stop] Start!");
this.druidDataSource.close();
if (null != this.dbConfManager) {
this.dbConfManager.stopDbConfManager();
}
if (null != this.dbPasswdManager) {
this.dbPasswdManager.stopDbPasswdManager();
}
logger.warn("[DataSource Stop] End!");
}
}
public void setSingleInGroup(boolean isSingleInGroup) {
this.runTimeConf.setSingleInGroup(isSingleInGroup);
}
public void setAppName(String appName) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset appName !");
}
this.appName = appName;
}
public void setDbKey(String dbKey) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset dbKey !");
}
this.dbKey = dbKey;
}
public void setLocalPasswd(String passwd) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset passwd !");
}
this.localConf.setPasswd(passwd);
}
public void setLocalConnectionProperties(Map<String, String> map) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset connectionProperties !");
}
this.localConf.setConnectionProperties(map);
String driverClass = map.get(TAtomConfParser.APP_DRIVER_CLASS_KEY);
if (!TStringUtil.isBlank(driverClass)) {
this.localConf.setDriverClass(driverClass);
}
}
public void setLocalDriverClass(String driverClass) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset driverClass !");
}
this.localConf.setDriverClass(driverClass);
}
public void setLocalSorterClass(String sorterClass) throws AtomAlreadyInitException {
if (initFalg) {
throw new AtomAlreadyInitException("[AlreadyInit] couldn't Reset sorterClass !");
}
this.localConf.setSorterClass(sorterClass);
}
public String getUnitName() {
return unitName;
}
public void setUnitName(String unitName) {
this.unitName = unitName;
}
public String getAppName() {
return appName;
}
public String getDbKey() {
return dbKey;
}
public TAtomDbStatusEnum getStatus() {
return this.runTimeConf.getDbStautsEnum();
}
public TAtomDbTypeEnum getDbType() {
return this.runTimeConf.getDbTypeEnum();
}
public void setDbStatusListeners(List<AtomDbStatusListener> dbStatusListeners) {
this.dbStatusListeners = dbStatusListeners;
}
private void processDbStatusListener(TAtomDbStatusEnum oldStatus, TAtomDbStatusEnum newStatus) {
if (null != oldStatus && oldStatus != newStatus) {
if (null != dbStatusListeners) {
for (AtomDbStatusListener statusListener : dbStatusListeners) {
try {
statusListener.handleData(oldStatus, newStatus);
} catch (Exception e) {
logger.error("[call StatusListenner Error] !", e);
continue;
}
}
}
}
}
}