Spring Boot(22)集成MyBatis Plus实现读写分离

Spring Boot(22)集成MyBatis Plus实现读写分离

文章目录

  • Spring Boot(22)集成MyBatis Plus实现读写分离
    • 前提
    • 方案
      • 方案一:拦截器自定义读写分离
      • 方案二:中间件
        • proxy:mycat
        • client:shardingsphere

前提

搭建mysql主从集群

方案

  1. 拦截器自定义实现:侵入性较高
  2. 中间件实现

方案一:拦截器自定义读写分离

思路:数据源抽象类AbstractRoutingDataSource#determineCurrentLookupKey,结合ThreadLocal,在切面根据CRUD决定数据源,然后mapper调用,对应数据源取决于上述determineCurrentLookupKey作为key对应的value(value就是一个DataSource)

规定:service,读接口以get或select作为前缀,写接口以add、insert、remove、delete、edit、update作为前缀

流程:

  1. 定义切面:根据读写切点,决定AbstractRoutingDataSource#determineCurrentLookupKey返回值

    ThreadLocal

    // 读切点
    @Pointcut("execution(* com.xl.demo.mysql.service.*.select*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.get*(..))")
    public void readPointcut() { }
    
    @Before("readPointcut()")
    public void read() {
        DbTypeContextHolder.slave();
    }
    
    // 写切点
    @Pointcut("execution(* com.xl.demo.mysql.service.*.insert*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.add*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.update*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.edit*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.delete*(..)) " +
              "|| execution(* com.xl.demo.mysql.service.*.remove*(..))")
    public void writePointcut() { }
    
    @Before("writePointcut()")
    public void write() {
        DbTypeContextHolder.master();
    }
    
  2. 定义DbTypeContextHolder类:持有当前数据源的key

    接口:get、master、slave(slave按轮询选择一个)

    public enum DbTypeEnum {
        MASTER,
        SLAVE1,
        SLAVE2,
        ;
    }
    
    private static final ThreadLocal<DbTypeEnum> contextHolder = new ThreadLocal<>();
    
    public static DbTypeEnum get() {
        return contextHolder.get();
    }
    
    public static void master() {
        contextHolder.set(DbTypeEnum.MASTER)
    }
    
    private static final AtomicInteger counter = new AtomicInteger(-1);
    public static void slave() {
        int index = counter.getAndIncrement() % 2;
        if (counter.get() > 9999) {
            counter.set(-1);
        }
        
        if (index == 0) {
            contextHolder.set(DbTypeEnum.SLAVE1)
        } else {
            contextHolder.set(DbTypeEnum.SLAVE2)
        }
    }
    

    到目前,通过拦截器,基于不同的请求(读或写)切换数据源的key,然后AbstractRoutingDataSource#determineCurrentLookupKey根据这个key决定数据源

  3. 实现AbstractRoutingDataSource抽象类,并注入到SqlSessionFactory

    1. 配置文件配置数据源

    2. 实现AbstractRoutingDataSource

      public class MyRoutingDataSource extends AbstractRoutingDataSource {
          @Nullable
          @Override
          protected Object determineCurrentLookupKey() {
              return DBContextHolder.get();
          }
      }
      
    3. 数据源Bean配置

      @Configuration
      public class DataSourceConfig {
      
          @Bean
          @ConfigurationProperties("spring.datasource.master")
          public DataSource masterDataSource() {
              return DataSourceBuilder.create().build();
          }
      
          @Bean
          @ConfigurationProperties("spring.datasource.slave1")
          public DataSource slave1DataSource() {
              return DataSourceBuilder.create().build();
          }
      
          @Bean
          public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                                @Qualifier("slave1DataSource") DataSource slave1DataSource) {
              Map<Object, Object> targetDataSources = new HashMap<>();
              targetDataSources.put(DbTypeEnum.MASTER, masterDataSource);
              targetDataSources.put(DbTypeEnum.SLAVE1, slave1DataSource);
      
              MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
              myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
              myRoutingDataSource.setTargetDataSources(targetDataSources);
              return myRoutingDataSource;
          }
      
      }
      
    4. SqlSessionFactory注入数据源

      @Configuration
      public class MyBatisConfig {
      
          @Resource(name = "myRoutingDataSource")
          private DataSource myRoutingDataSource;
      
          @Bean
          public SqlSessionFactory sqlSessionFactory() throws Exception {
              MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
              factoryBean.setDataSource(myRoutingDataSource);
              return factoryBean.getObject();
          }
      
      }
      

补充:存在一些场景,刚写就需要查询,所以新增注解@Master,在切面中,写切点加入该注解,读切点去除该注解

方案二:中间件

proxy:mycat

步骤:

  1. 安装mycat并配置读写分离
  2. Spring Boot的数据源改为mycat
client:shardingsphere

步骤:

  1. 添加依赖
  2. 配置

refer:

SpringBoot结合ShardingSphere实现主从读写分离

你可能感兴趣的:(框架,spring,boot,mysql)