记一次多数据源主从相关配置的调研

384 阅读1分钟

多数据源, 不考虑主从, 分库分表的情况下

采用自定义注解+AOP+AbstractRoutingDataSource实现

  1. 初始化时, 实例化需要的数据源, 并塞到map中(yml配置好数据源即可)
    @Bean
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource() {
        LOGGER.info("first 数据源初始化成功!!!");
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource() {
        LOGGER.info("second 数据源初始化成功!!!");
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
        // 用于动态切换数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.FIRST, firstDataSource);
        targetDataSources.put(DataSourceType.SECOND, secondDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
  1. 主要切面方法如下, 根据注解的type()动态切换数据源
    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        if (ds == null) { // 默认采用First这个数据源
            DynamicDataSource.setDataSource(DataSourceType.FIRST);
        } else {
            DynamicDataSource.setDataSource(ds.type());
        }
        LOGGER.info("当前接口使用的数据源是 "  + DynamicDataSource.getDataSource());
        try {
            return point.proceed();
        } finally { // ThreadLocal remove
            DynamicDataSource.clearDataSource();
        }
    }
  1. 使用方式: 直接使用注解即可
    @Override
    @DataSource(type = DataSourceType.FIRST)
    public List<Demo> getFirst() {
        return list();
    }

    @Override
    @DataSource(type = DataSourceType.SECOND)
    public List<Demo> getSecond() {
        return list();
    }

考虑主从的情况下, 每套主从数据源, 都持有自己mapper逻辑

  1. 最重要的是配置yml
sharding:
  jdbc:
    datasource: # 好像pg和mysql不兼容???
      names: my-master,my-slave,pg-master,pg-slave # 不能含有空格
      my-master: mysql主库配置
      my-slave: mysql从库配置
      pg-master: pg主库配置
      pg-slave: pg从库配置
    config:
      props:
        sql:
          show: true
      sharding:
        master-slave-rules:
          first:
            masterDataSourceName: my-master
            slaveDataSourceNames:
              - my-slave
            loadBalanceAlgorithmType: ROUND_ROBIN
          second:
            masterDataSourceName: pg-master
            slaveDataSourceNames:
              - pg-slave
            loadBalanceAlgorithmType: ROUND_ROBIN
        default-data-source-name: first
        default-database-strategy:
          hint:
            algorithm-class-name: cn.az.project.alg.MyDataSourceRoutingAlgorithm
  1. 实现routing算法 HintShardingAlgorithm
    @Override
    @SuppressWarnings("unchecked")
    public Collection<String> doSharding(Collection<String> availableTargetNames, ShardingValue shardingValue) {
        LOGGER.info("shardingValue=" + shardingValue);
        LOGGER.info("availableTargetNames=" + availableTargetNames);
        List<String> shardingResult = new ArrayList<>();
        ListShardingValue<String> tmpSharding = (ListShardingValue<String>) shardingValue;
        for (String value : tmpSharding.getValues()) {
            if (availableTargetNames.contains(value)) {
                shardingResult.add(value);
            }
        }
        return shardingResult;
    }
  1. 实现切面, 针对每个主从数据源
    @Pointcut("execution(* cn.az.project.mapper.first..*.*(..))")
    public void switchFirst() {
    }

    @Before("switchFirst()")
    public void switchFirstBefore() {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setDatabaseShardingValue(DataSourceType.FIRST);
        logger.info("切换到MYSQL数据源~");
    }

    /**
     * 恢复默认数据源
     */
    @After("switchFirst()")
    public void switchFirstAfter() {
        //清理掉当前设置的数据源,让默认的数据源不受影响
        HintManager hintManager = HintManagerHolder.get();
        if (hintManager != null) {
            hintManager.close();
        }
    }

项目地址, 参考这里

主要用于个人纪录学习内容, 还请多多关照