Sharding-JDBC分库分表-自动配置与分片规则加载原理-3

Sharding JDBC自动配置的原理

与所有starter一样,shardingsphere-jdbc-core-spring-boot-starter也是通过SPI自动配置的原理实现分库分表配置加载,spring.factories文件中的自动配置类shardingsphere-jdbc-core-spring-boot-starter功不可没,他主要是自动创建了模式bean、事务类型bean和数据源bean,配置加载的过程可以归纳如下:
创建模式bean 创建数据源bean 创建事务类型扫描bean ShardingSphereAutoConfiguration ModeConfiguration ShardingSphereDataSource TransactionTypeScanner

其中,创建数据源bean时会根据不同的模式创建不同的bean,本地模式直接从配置文件中加载,配置中心模式就从配置中心加载。ShardingSphereAutoConfiguration的实现如下:

java 复制代码
@Configuration
@ComponentScan("org.apache.shardingsphere.spring.boot.converter")
@EnableConfigurationProperties(SpringBootPropertiesConfiguration.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@RequiredArgsConstructor
public class ShardingSphereAutoConfiguration implements EnvironmentAware {
    
    private String databaseName;
    
    private final SpringBootPropertiesConfiguration props;
    
    private final Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();
    
    /**
     * 模式配置
     *
     * @return mode configuration
     */
    @Bean
    public ModeConfiguration modeConfiguration() {
        return null == props.getMode() ? null : new YamlModeConfigurationSwapper().swapToObject(props.getMode());
    }
    
    /**
     * 单机模式:从本地配置中加载DataSource
     *
     * @param rules rules configuration
     * @param modeConfig mode configuration
     * @return data source bean
     * @throws SQLException SQL exception
     */
    @Bean
    @Conditional(LocalRulesCondition.class)
    @Autowired(required = false)
    public DataSource shardingSphereDataSource(final ObjectProvider<List<RuleConfiguration>> rules, final ObjectProvider<ModeConfiguration> modeConfig) throws SQLException {
        Collection<RuleConfiguration> ruleConfigs = Optional.ofNullable(rules.getIfAvailable()).orElseGet(Collections::emptyList);
        return ShardingSphereDataSourceFactory.createDataSource(databaseName, modeConfig.getIfAvailable(), dataSourceMap, ruleConfigs, props.getProps());
    }
    
    /**
     * 集群模式:从配置中心中加载DataSource
     *
     * @param modeConfig mode configuration
     * @return data source bean
     * @throws SQLException SQL exception
     */
    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource dataSource(final ModeConfiguration modeConfig) throws SQLException {
        return !dataSourceMap.isEmpty() ? ShardingSphereDataSourceFactory.createDataSource(databaseName, modeConfig, dataSourceMap, Collections.emptyList(), props.getProps())
                : ShardingSphereDataSourceFactory.createDataSource(databaseName, modeConfig);
    }
    
    /**
     * 事务类型扫描bean
     *
     * @return transaction type scanner
     */
    @Bean
    public TransactionTypeScanner transactionTypeScanner() {
        return new TransactionTypeScanner();
    }
    
    @Override
    public final void setEnvironment(final Environment environment) {
        dataSourceMap.putAll(DataSourceMapSetter.getDataSourceMap(environment));
        databaseName = DatabaseNameSetter.getDatabaseName(environment);
    }
}

下面以单机模式出发点,理一下加载的过程。

java 复制代码
@Bean
@Conditional(LocalRulesCondition.class)
@Autowired(required = false)
public DataSource shardingSphereDataSource(final ObjectProvider<List<RuleConfiguration>> rules, final ObjectProvider<ModeConfiguration> modeConfig) throws SQLException {
    Collection<RuleConfiguration> ruleConfigs = Optional.ofNullable(rules.getIfAvailable()).orElseGet(Collections::emptyList);
    // 通过ShardingSphereDataSourceFactory工厂创建数据源
    return ShardingSphereDataSourceFactory.createDataSource(databaseName, modeConfig.getIfAvailable(), dataSourceMap, ruleConfigs, props.getProps());
}

通过对源码的跟踪,可以发现,ShardingSphereDataSourceFactory.createDataSource创建数据源经历了如下的过程
创建数据源bean new 创建上下文管理器 创建JDBC上下文 创建InstanceMetaData 根据mode调用 创建元数据上下文 创建数据库连接元数据 调用 加载分片规则 new ShardingSphereAutoConfiguration ShardingSphereDataSourceFactory.createDataSource ShardingSphereDataSource ShardingSphereDataSource.createContextManager JDBCContext InstanceMetaDataBuilderFactory.create StandaloneContextManagerBuilder.build MetaDataContextsFactory.create ShardingSphereDatabasesFactory.create ShardingSphereDatabase.create DatabaseRulesBuilder.build ShardingSphereDatabase

分片规则加载原理

分片规则、审计规则、key生成规则都是通过SPI的方式加载,自动配置类ShardingSphereAutoConfiguration中创建ShardingSphereDataSource的时候,会加载配置的分片规则,创建核心配置类ShardingRule,在ShardingRule的创建中会通过SPI的方式加载分片规则。加载的过程如下:
创建数据源bean 查询实例 通过SPI Class获取服务实例 Y:创建新的service实例 N:Map中获取service单例 创建分片规则 创建分片规则 创建核心分库分表规则 分片规则 Key生成规则 审计规则 通过工厂创建分片算法 获取注册的service SPI方式获取service 通过工厂创建key生成算法 通过工厂创建审计算法 ShardingSphereAutoConfiguration ShardingSphereDataSource 一系列加载... DatabaseRulesBuilder.build OrderedSPIRegistry.getRegisteredServices ShardingSphereServiceLoader.getServiceInstances(spiClass) 带@SingletonSPI注解? service.getClass().getDeclaredConstructor().newInstance() SERVICES.getOrDefault(serviceInterface, Collections.emptyList()) ShardingRuleBuilder.build new ShardingRule ShardingAlgorithmFactory.newInstance KeyGenerateAlgorithmFactory.newInstance ShardingAuditAlgorithmFactory.newInstance ShardingSphereAlgorithmFactory.createAlgorithm TypedSPIRegistry.getRegisteredService ShardingSphereServiceLoader.getServiceInstances(spiClass)

SPI核心实现类ShardingSphereServiceLoader中会将SPI接口进行Map缓存管理,需要时直接获取。如果Map中不存在,就通过反射的方式新建服务实例,具体实现源码如下:

java 复制代码
public final class ShardingSphereServiceLoader {
    
    // 缓存service实例,
    // 缓存的Key,如:
    //  org.apache.shardingsphere.sharding.spi.ShardingAlgorithm
    //  org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm
    //  org.apache.shardingsphere.transaction.spi.ShardingSphereTransactionManager
    private static final Map<Class<?>, Collection<Object>> SERVICES = new ConcurrentHashMap<>();
    
    /**
     * 注册服务实例
     *
     * @param 服务接口
     */
    public static void register(final Class<?> serviceInterface) {
        if (!SERVICES.containsKey(serviceInterface)) {
            SERVICES.put(serviceInterface, load(serviceInterface));
        }
    }
    
    private static <T> Collection<Object> load(final Class<T> serviceInterface) {
        Collection<Object> result = new LinkedList<>();
        for (T each : ServiceLoader.load(serviceInterface)) {
            result.add(each);
        }
        return result;
    }
    
    /**
     * 获取服务实例
     * 
     * @param 服务接口
     * @param <T> 服务类型
     * @return 服务实例
     */
    public static <T> Collection<T> getServiceInstances(final Class<T> serviceInterface) {
        return null == serviceInterface.getAnnotation(SingletonSPI.class) ? createNewServiceInstances(serviceInterface) : getSingletonServiceInstances(serviceInterface);
    }
    
    @SneakyThrows(ReflectiveOperationException.class)
    @SuppressWarnings("unchecked")
    private static <T> Collection<T> createNewServiceInstances(final Class<T> serviceInterface) {
        if (!SERVICES.containsKey(serviceInterface)) {
            return Collections.emptyList();
        }
        Collection<Object> services = SERVICES.get(serviceInterface);
        if (services.isEmpty()) {
            return Collections.emptyList();
        }
        Collection<T> result = new LinkedList<>();
        for (Object each : services) {
            // 通过反射新建实例
            result.add((T) each.getClass().getDeclaredConstructor().newInstance());
        }
        return result;
    }
    
    @SuppressWarnings("unchecked")
    private static <T> Collection<T> getSingletonServiceInstances(final Class<T> serviceInterface) {
        return (Collection<T>) SERVICES.getOrDefault(serviceInterface, Collections.emptyList());
    }
}
相关推荐
ezreal_pan4 天前
ShardingSphere-Proxy 连接实战:从 Golang 原生 SQL 到 GORM 的应用
golang·shardingsphere·分库分表
vivo互联网技术19 天前
OceanBase 的探索与实践
mysql·oceanbase·分布式数据库·分库分表·tidb迁移
JingAi_jia9171 个月前
【源码】Sharding-JDBC源码分析之SQL路由及SingleSQLRouter单表路由
分库分表·分片路由·springboot分库分表·shardingjdbc源码·spring分库分表·singlesqlrouter·单表路由
JingAi_jia9172 个月前
【源码】Sharding-JDBC源码分析之Sql解析的原理
分库分表·sharding-jdbc·1024程序员节·sharding jdbc·antlr·springboot分库分表·sql解析原理·mysqlstatement
Dylanioucn2 个月前
【分布式微服务云原生】深入探究:多分片键下的分库分表策略
数据库·分布式·微服务·云原生·分库分表
阿维的博客日记3 个月前
图文并茂解释水平分表,垂直分表,水平分库,垂直分库
数据库·分库分表
JingAi_jia9174 个月前
【源码】Sharding-JDBC源码分析之ContextManager创建中ShardingSphereDatabase的创建原理
database·分库分表·sharding-jdbc·sharding jdbc·springboot分库分表·shardingjdbc源码
wuweijie@apache.org4 个月前
IntelliJ IDEA 集成 ShardingSphere-JDBC 访问分库分表
ide·intellij-idea·shardingsphere·分库分表
JingAi_jia9174 个月前
【源码】Sharding-JDBC源码分析之Yaml分片配置原理(一)
分库分表·sharding-jdbc·sharding jdbc·springboot分库分表·分库分表配置·分库分表配置原理