baomidou / dynamic-datasource

dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务
https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
Apache License 2.0
4.75k stars 1.19k forks source link

适配 shardingsphere,启动时初始化分库的多个数据源 #625

Closed ykgarfield closed 9 months ago

ykgarfield commented 10 months ago

Please fill it out carefully, or it will be closed. 请认真填写,不然会直接关闭。

Enviroment

JDK Version(required): 17

SpringBoot Version(required): 2.5.12

dynamic-datasource-spring-boot-starter Version(required): 4.2.0

druid Version(optional):

Describe what happened

使用 shardingsphere 5.3.2 ,结合 dynamic-datasource,做主库和分库, shardingsphere 使用了如下的形式配置数据源:

url: jdbc:shardingsphere:classpath:shardingsphere.yaml
driverClassName: org.apache.shardingsphere.driver.ShardingSphereDriver

shardingsphere.yaml 配置大概如下:

dataSources:
  # 4 个分库
  test_0:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: xxx
  test_1:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: xxx
  test_2:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: xxx
  test_3:
    dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: xxx

dynamic-datasource 的配置如下:

spring:
  datasource:
    dynamic:
      primary: main # 通过此属性指定主属性源名称
      datasource:
        # 主数据源配置, 不需要进行分库分表
        main:
          url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&zeroDateTimeBehavior=convertToNull&autoReconnect=true&serverTimezone=Asia/Shanghai
          username: xxx
          password: xxx
          driverClassName: com.mysql.jdbc.Driver
        # 分库分表数据源配置
        sharding:
          url: jdbc:shardingsphere:classpath:shardingsphere.yaml
          driverClassName: org.apache.shardingsphere.driver.ShardingSphereDriver
          druid:
            validationQuery: select 1

项目启动之后,只是简单的初始化了 sharding 这个数据源,并不会去实际初始化 test_0 ~ test_3 这几个数据源。只有在第一次访问分库的时候才会去初始化这几个数据源,这就导致了第一次访问某个接口会比较慢(需要初始化多个数据源)。

Expected Result: 项目启动之后可以去初始化分库的数据源。

大概的思路如下,当前在项目中对 dynamic-datasource 的代码做了一些扩展:

ShardingDruidDataSourceCreator 继承 DruidDataSourceCreator, 在 createDataSource 方法中调用一次 getConnection() 方法,这样可以去初始化分库的数据源。

@Slf4j
public class ShardingDruidDataSourceCreator extends DruidDataSourceCreator {

    public ShardingDruidDataSourceCreator() {
    }

    public ShardingDruidDataSourceCreator(DruidConfig gConfig, DruidFilterCallBack druidFilterCallBack) {
        super(gConfig, druidFilterCallBack);
    }

    @Override
    public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
        DataSource dataSource = super.createDataSource(dataSourceProperty);

        try {
            // 获取连接进行初始化
            Connection connection = dataSource.getConnection();
            connection.close();
            log.info("初始化分库分表数据源成功!");
        } catch (SQLException e) {
            throw new RuntimeException("数据源连接失败,请检查配置!", e);
        }

        return dataSource;
    }

    @Override
    public boolean support(DataSourceProperty dataSourceProperty) {
        String jdbcUrl = dataSourceProperty.getUrl();
        // jdbc url 以 jdbc:shardingsphere 开头
        return StringUtils.startsWith(jdbcUrl, "jdbc:shardingsphere");
    }

}

ShardingDataSourceCreatorConfig 会创建上面的 ShardingDruidDataSourceCreator bean.

@AutoConfigureBefore(value = DynamicDataSourceAutoConfiguration.class, name = "com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration")
@Configuration
@Component
public class ShardingDataSourceCreatorConfig {
    // 在 DruidDataSourceCreator 之前初始化,这样在创建数据源的时候会优先执行 ShardingDruidDataSourceCreator
    public static final int SHARDING_SPHERE_ORDER = DynamicDataSourceCreatorAutoConfiguration.DRUID_ORDER - 1;

    @ConditionalOnClass(DruidDataSource.class)
    @Configuration
    @Slf4j
    static class DruidDataSourceCreatorConfiguration {

        @Autowired(required = false)
        private ApplicationContext applicationContext;

        @Bean
        @Order(SHARDING_SPHERE_ORDER)
        public ShardingDruidDataSourceCreator shardingDruidDataSourceCreator(DynamicDataSourceProperties properties) {
            DruidConfig druid = properties.getDruid();
            return new ShardingDruidDataSourceCreator(druid, proxyFilters -> {
                List<Filter> filters = new ArrayList<>();
                if (applicationContext != null && DsStrUtils.hasText(proxyFilters)) {
                    for (String filterId : proxyFilters.split(",")) {
                        try {
                            filters.add(applicationContext.getBean(filterId, Filter.class));
                        } catch (Exception e) {
                            log.warn("dynamic-datasource cannot load druid filter with name [{}], will be ignored", filterId);
                        }
                    }
                }
                return filters;
            });
        }
    }
}

不知道 dynamic-datasource 官方是否可以适配支持此特性。

Steps to reproduce

huayanYu commented 9 months ago

可以用event去做,查看一下源码包下的event