SpringBoot通过AbstractRoutingDataSource实现读写分离、读负载,你学废了吗!

1、配置一主两从数据库

具体操作过程:

CentOS7下的linux的MySQL5.7主从数据库的配置

地址: https://blog.youkuaiyun.com/m0_37459945/article/details/78577105?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1.control&dist_request_id=1328680.64181.16164973606295389&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1.control

2、具体过程

整体介绍:

1.创建数据库,并配置多数据源2.创建对应的 EntityMapper,Service,Controller3.根据“AbstractRoutingDataSource”类去实现子类“DynamicDataSource”。重点重写方法是“determineCurrentLookupKey”4.通过代码的方式实例化DataSource和DynamicDataSource5.创建注解“DynaminDataSource”,并为该注解编写切面类“DataSourceAspect”;

1. 创建数据库,并配置多数据源

•创建数据库SQL

图片

•多数据源配置

图片

2. 创建对应的 EntityMapper,Service,Controller

不进行详细讲解,有经验的小伙伴都会!

图片

3. 根据“AbstractRoutingDataSource”类去实现子类“DynamicDataSource”

AbstractRoutingDataSource分析的讲解参考博客 AbstractRoutingDataSource分析(https://www.jianshu.com/p/edc282e6bbb9) 

具体代码:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;import java.util.Map;/** * 扩展 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法 * 动态数据源 * determineCurrentLookupKey() 方法决定使用哪个数据源 * * @author RedAnts */public class DynamicDataSource extends AbstractRoutingDataSource {    /**     * ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。     * 也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于线程的 private static 类型变量。     */    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();    /**     * 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好     *     * @param defaultTargetDataSource 默认数据源     * @param targetDataSources       目标数据源     */    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {        super.setDefaultTargetDataSource(defaultTargetDataSource);        super.setTargetDataSources(targetDataSources);        super.afterPropertiesSet();    }    @Override    protected Object determineCurrentLookupKey() {        return getDataSource();    }    public static void setDataSource(String dataSource) {        CONTEXT_HOLDER.set(dataSource);    }    public static String getDataSource() {        return CONTEXT_HOLDER.get();    }    public static void clearDataSource() {        CONTEXT_HOLDER.remove();    }}

4. 通过代码的方式实例化DataSource和DynamicDataSource

直接上代码

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import javax.sql.DataSource;import java.util.HashMap;import java.util.Map;/** * 配置多数据源 * * @author RedAnts */@Configurationpublic class DynamicDataSourceConfig {    @Bean    @ConfigurationProperties("spring.datasource.druid.master")    public DataSource masterDataSource() {        return DruidDataSourceBuilder.create().build();    }    @Bean    @ConfigurationProperties("spring.datasource.druid.slave1")    public DataSource slave1DataSource() {        return DruidDataSourceBuilder.create().build();    }    @Bean    @ConfigurationProperties("spring.datasource.druid.slave2")    public DataSource slave2DataSource() {        return DruidDataSourceBuilder.create().build();    }    @Bean    @Primary    public DynamicDataSource dataSource() {        Map targetDataSources = new HashMap<>(5);        targetDataSources.put(DataSourceNames.MASTER.getKey(), masterDataSource());        targetDataSources.put(DataSourceNames.SLAVE1.getKey(), slave1DataSource());        targetDataSources.put(DataSourceNames.SLAVE2.getKey(), slave2DataSource());        return new DynamicDataSource(masterDataSource(), targetDataSources);    }}

5. 创建注解“DynaminDataSource”,并为该注解编写切面类“DataSourceAspect”;

直接上代码

/** * 多数据源注解 * 

* 指定要使用的数据源,如果指定多数据源随机使用 * * @author RedAnts */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DynaminDataSource { String[] names() default {};}
import cn.hutool.core.util.RandomUtil;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.core.Ordered;import org.springframework.stereotype.Component;import java.lang.reflect.Method;/** * 多数据源,切面处理类 * * @author RedAnts */@Slf4j@Aspect@Componentpublic class DataSourceAspect implements Ordered {    @Pointcut("@annotation(cn.guangboyuan.dynamindatasource.config.datasource.DynaminDataSource)")    public void dataSourcePointCut() {    }    @Around("dataSourcePointCut()")    public Object around(ProceedingJoinPoint point) throws Throwable {        MethodSignature signature = (MethodSignature) point.getSignature();        Method method = signature.getMethod();        DynaminDataSource ds = method.getAnnotation(DynaminDataSource.class);        if (ds == null) {            DynamicDataSource.setDataSource(DataSourceNames.MASTER.getKey());            log.debug("set datasource is " + DataSourceNames.MASTER.getKey());        } else {            String[] names = ds.names();            String target = DataSourceNames.MASTER.getKey();            if (1 == names.length) {                target = names[0];                DynamicDataSource.setDataSource(target);            } else if (2 == names.length) {                int randomInt = RandomUtil.randomInt(0, 2);                target = names[randomInt];                DynamicDataSource.setDataSource(target);            } else {                DynamicDataSource.setDataSource(target);            }            log.debug("set datasource is " + target);        }        try {            return point.proceed();        } finally {            DynamicDataSource.clearDataSource();            log.debug("clean datasource");        }    }    @Override    public int getOrder() {        return 1;    }}

注:DataSourceAspect 切面类中有使用随机数实现对两个从库的负载

6. 在Service层的实现类上使用注解

图片

 需要资料的小伙伴,点赞加收藏,关注我之后添加小助理vx:bjmsb0606006 即可获取免费下载方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值