多数据源配置
# 主库数据源配置
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://192.168.1.11:3308/redenvelopes?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&allowMultiQueries=true
spring.datasource.master.username=redenvelopes
spring.datasource.master.password=USSfro2Y2vSoxj4nOH8JA4zk9tmINs7i6bOKQgcu0cQ1SFHP74CIGdHzsxZaQlDAHxRs7MMkj1ilDeLAnOBBzw==
spring.datasource.master.initial-size=30
spring.datasource.master.max-active=300
spring.datasource.master.min-idle=20
spring.datasource.master.time-between-eviction-runs-millis=60000
spring.datasource.master.min-evictable-idle-time-millis=300000
spring.datasource.master.validation-query=select 1
spring.datasource.master.test-while-idle=true
spring.datasource.master.test-on-borrow=true
spring.datasource.master.test-on-return=false
spring.datasource.master.max-wait=60000
spring.datasource.master.filters=stat,config
spring.datasource.master.filter.stat.enabled=true
spring.datasource.master.filter.stat.slow-sql-millis=100000
spring.datasource.master.filter.stat.log-slow-sql=true
spring.datasource.master.connection-properties=config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALPq2OsSVb075O3ELXlC82KF2YdKtVv01y4tu7wZN11b3mw6zZDIdh+ZRU3S+Kui6wVE7XRsupbroIJcspW40eMCAwEAAQ==
# 从库数据源配置
spring.datasource.readonly.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.readonly.url=jdbc:mysql://192.168.1.11:3308/redenvelopes?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.readonly.username=devsup01
spring.datasource.readonly.password=DaSw+Rg9zM508O+H5b2a32mE0m6Qy1v2YtSl0ti6QTtJOXjKWsQhkdCDIRiK9gV1/hY7hYrpGe403yom+um4PQ==
spring.datasource.readonly.initial-size=30
spring.datasource.readonly.max-active=300
spring.datasource.readonly.min-idle=20
spring.datasource.readonly.time-between-eviction-runs-millis=60000
spring.datasource.readonly.min-evictable-idle-time-millis=300000
spring.datasource.readonly.validation-query=select 1
spring.datasource.readonly.test-while-idle=true
spring.datasource.readonly.test-on-borrow=true
spring.datasource.readonly.test-on-return=false
spring.datasource.readonly.max-wait=60000
spring.datasource.readonly.filters=stat,config
spring.datasource.readonly.filter.stat.enabled=true
spring.datasource.readonly.filter.stat.slow-sql-millis=100000
spring.datasource.readonly.filter.stat.log-slow-sql=true
spring.datasource.readonly.connection-properties=config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMzWx3SAo7gF2TguEJQjL5TFON47dTAJCU0Va3oygJ34OJRHjlPeOcla7eaxp40DlhH/MprUlgmLWCvXNEtEL8UCAwEAAQ==
配置DataSource
@Configuration
public class DataSourceConfig {
public final static String DS_MASTER = "master";
public final static String DS_READONLY = "readonly";
protected Logger logger = LoggerFactory.getLogger(getClass());
@Bean(name = DS_MASTER)
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource dataSource1() {
logger.info("Load datasource of {} ...", DS_MASTER);
return DruidDataSourceBuilder.create().build();
}
@Bean(name = DS_READONLY)
@ConfigurationProperties(prefix = "spring.datasource.readonly")
public DataSource dataSource2() {
logger.info("Load datasource of {} ...", DS_READONLY);
return DruidDataSourceBuilder.create().build();
}
@Autowired
@Lazy
@Qualifier(DS_MASTER)
DataSource master;
@Autowired
@Lazy
@Qualifier(DS_READONLY)
DataSource readonly;
@Bean
@Primary
public DataSource dataSource() {
RdpRoutingDataSource rdpRoutingDataSource = new RdpRoutingDataSource();
rdpRoutingDataSource.setDefaultTargetDataSource(master);
Map<Object, Object> dsMap = new HashMap<>(5);
dsMap.put(DS_MASTER, master);
dsMap.put(DS_READONLY, readonly);
rdpRoutingDataSource.setTargetDataSources(dsMap);
return rdpRoutingDataSource;
}
动态选择DataSource
动态数据源线程上下文
public class DataSourceContextHolder {
protected static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);
public static final String DEFAULT_DS = "master";
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDB(String dbType) {
logger.debug("Try to select datasource:{}", dbType);
contextHolder.set(dbType);
}
public static String getDB() {
return (contextHolder.get());
}
public static void clearDB() {
logger.debug("Try to clear datasource:{}", getDB());
contextHolder.remove();
}
}
自定义类继承 AbstractRoutingDataSource ,重写 determineCurrentLookupKey() ,返回数据源的唯一标识;
public class RdpRoutingDataSource extends AbstractRoutingDataSource {
protected static Logger logger = LoggerFactory.getLogger(RdpRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
logger.debug("当前数据源: {}", StringUtils.isEmpty(DataSourceContextHolder.getDB())? "default" : DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
注解(主动配置选择)
用于主动选择数据源
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DsSelector {
String value() default DataSourceContextHolder.DEFAULT_DS;
}
AOP 拦截切换数据源
每次对@DsSelector注解的方法去数据源上下文中获取当前线程的连接的数据源
@EnableAspectJAutoProxy
@Order(1)
@Aspect
@Component
public class RdpDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(RdpDataSourceAspect.class);
@Around("execution(* cn.swiftpass..service.impl.*.*(..)) && @annotation(DsSelector)")
public Object around(ProceedingJoinPoint joinPoint){
try {
String dataSource = "";
Class<?> className = joinPoint.getTarget().getClass();
String methodName = joinPoint.getSignature().getName();
Class<?>[] argClass = ((MethodSignature)joinPoint.getSignature()).getParameterTypes();
try {
Method method = className.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DsSelector.class)) {
DsSelector annotation = method.getAnnotation(DsSelector.class);
dataSource = annotation.value();
}
} catch (Exception e) {
logger.warn("RdpDataSourceAspect.around exception", e);
}
DataSourceContextHolder.setDB(dataSource);
return joinPoint.proceed();
} catch (Throwable t) {
logger.error("未知异常", t);
return null;
} finally {
DataSourceContextHolder.clearDB();
}
}
}
|