# dynamin-datasource **Repository Path**: redants-101/dynamin-datasource ## Basic Information - **Project Name**: dynamin-datasource - **Description**: SpringBoot+MybatisPlus 主从数据库(一主两从),通过注解的方式实现读写分离(读在两个从库随机选择)。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-06-24 - **Last Updated**: 2021-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README SpringBoot通过AbstractRoutingDataSource实现读写分离、读负载,你学废了吗! SpringBoot通过AbstractRoutingDataSource实现读写分离,一主两从 ,通过注解读写分离。注: 读的时候再两个从库之间随机获取DataSource。 1、配置一主两从数据库 具体操作过程: CentOS7下的linux的MySQL5.7主从数据库的配置 参考地址: https://blog.csdn.net/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,Controller 3.根据“AbstractRoutingDataSource”类去实现子类“DynamicDataSource”。重点重写方法是“determineCurrentLookupKey” 4.通过代码的方式实例化DataSource和DynamicDataSource 5.创建注解“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 */ @Configuration public 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) @Documented public @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 @Component public 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层的实现类上使用注解 全部代码,已经推到Gitee Gitee地址 https://gitee.com/fuchenggang/dynamin-datasource