作者:红箭777_387 | 来源:互联网 | 2023-09-14 15:46
有使用注解,或者使用代理的.
首先是代码层面的
由于简单,使用了注解的方式来做读写分离
后面会讲mysql层面主从的配置
Spring为我们提供了一个类org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
核心思想就是通过AOP拿到注解上的数据源,然后在AbstractRoutingDataSource 的实现类中将数据源返回对应的数据源
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
LocalDS ds = CustomerContextHolder.getLocalDS();
logger.info("返回数据源对象【" + ds + "】");
if(ds != null){
return ds.getCustomerType();
}
return null;
}
}
public enum CustomerType {
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE;
}
AOP拦截
@Aspect
@Order(-10000)
@Component
public class AopDatasource {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 添加业务逻辑方法切入点
*/
@Pointcut("execution(* com.njq.nongfadai.service..*.*(..))")
public void dynamicDS() {
}
/**
*
* Description: 请添加方法说明: 处理事务之前动态切换数据源
* @param point
* @throws Throwable 参数
*/
@Before("dynamicDS()")
public void dynamicDSSwitchBefore(JoinPoint point) throws Throwable {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class> classz = target.getClass(); // 获取目标类
Class>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
LocalDS ds = CustomerContextHolder.getLocalDS();
if(ds == null){
ds = new LocalDS(CustomerType.MASTER, 0);
logger.info("默认主数据源");
}
try {
Method m = classz.getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DynDatasource.class)) {
DynDatasource data = m.getAnnotation(DynDatasource.class);
logger.info("用户选择数据库库类型:" + data.value());
ds.setCustomerType(data.value());// 数据源放到当前线程中
}
} catch (Exception e) {
logger.error(classz.getName() + "." + method + " 动态切换数据源异常:", e);
} finally{
// 设置数据源
logger.info("设置数据源【{}】", ds);
CustomerContextHolder.setLocalDS(ds); // 数据源放到当前线程中
}
}
/**
*
* Description: 请添加方法说明: 处理事务之后清除动态数据源
* 不论正常执行还是异常退出都会执行
*/
@After("dynamicDS()")
public void dynamicDSSwitchAfter(){
LocalDS ds = CustomerContextHolder.getLocalDS();
logger.info("清空当前线程数据源【{}】",ds);
CustomerContextHolder.clearLocalDS();
}
}
DynDatasource 注解如下
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface DynDatasource {
/**
* 默认走主库
* Description: 请添加方法说明:
* @return 参数
*/
CustomerType value() default CustomerType.MASTER;
}
关于数据源的配置
Mysql层面的搭建
首先得介绍Mysql的集群
Mysql主主互备架构图如下
使用Keepalived来监控DB1和DB2的运行状态,同时维护一个VIP,此IP用来对外提供连续服务.详细的可见对Keepalived的介绍
MMM架构
这里是使用了MMM套件来进行管理,需要5个IP地址,两个Master节点各有一个固定不变的物理IP地址,另外还有两个只读IP和一个可写IP,这三个虚拟IP(两个读IP和一个写IP)不会固定在任何一个节点上,相反,它会在两个Master节点之间来回切换,如何切换取决于可用性
在双Master节点的基础上,增加多个Slave节点,即可实现双主多从节点应用架构
MMM的优势就是不仅可以监控两个Master节点的运行状态,还可以监控多个Slave节点的运行状态,整个切换过程完全不需要手工更改同步复制的配置,如果使用Keepalived的话,还需要手工写脚本监控每个节点的运行状态
Amoeba是一个分布式数据库的前端代理,主要是在应用层访问Mysql的时候充当SQL路由的功能,具有负载均衡,高可用,SQL过滤,读写分离等功能.
当然该架构比较复杂,后面有时间再实践一下吧