Shardingsphere(5.0.0.beta)源码学习-元数据上下文
上下文对象
上下文对象作为读取并解析配置, 承载数据的核心;
spring有ApplicationContext,netty有HandlerContext
向其他中间件一样,在ShardingSphere也十分重要; 后续几乎所有功能比如数据分片、数据加密、SQL 改写,各类扩展都依赖上下文对象存储的数据
标准元数据上下文 StandardMetaDataContexts
核心存储了ShardingSphereMetaData元数据集合,作为重点后面分析
@Getter
public final class StandardMetaDataContexts implements MetaDataContexts {
// 元数据集合
private final Map<String, ShardingSphereMetaData> metaDataMap;
private final ShardingSphereRuleMetaData globalRuleMetaData;
// 执行引擎
private final ExecutorEngine executorEngine;
//优化引擎上下文工厂
private final OptimizeContextFactory optimizeContextFactory;
private final ConfigurationProperties props;
// 状态上下文
private final StateContext stateContext;
...
}
治理元数据上下文 StandardMetaDataContexts
其实也是用的标准的StandardMetaDataContexts,治理模块shardingsphere-governance使用该上下文,通过配置中心读取规则配置,
注入到StandardMetaDataContexts中,GovernanceFacade是配置中心的门面模式,目前支持了zookeeper和etcd,其实还可以通过RegistryCenterRepository
的spi实现其他的配置中心,比如nacos, apollo,consul等。
public final class GovernanceMetaDataContexts implements MetaDataContexts {
//治理: 配置中心的门面模式
private final GovernanceFacade governanceFacade;
//还是使用StandardMetaDataContexts 装饰器模式
private volatile StandardMetaDataContexts metaDataContexts;
private final ShardingSphereLock lock;
...
}
初始化上下文对象
ShardingSphere中通过
org.apache.shardingsphere.infra.context.metadata.MetaDataContextsBuilder
构造器模式,构造
org.apache.shardingsphere.infra.context.metadata.MetaDataContexts
public ShardingSphereDataSource(final Map<String, DataSource> dataSourceMap, final Collection<RuleConfiguration> configurations, final Properties props) throws SQLException {
// 构建器构造 元数据上下文
metaDataContexts = new MetaDataContextsBuilder(
Collections.singletonMap(DefaultSchema.LOGIC_NAME, dataSourceMap), Collections.singletonMap(DefaultSchema.LOGIC_NAME, configurations), props).build();
// xa事务类型
String xaTransactionMangerType = metaDataContexts.getProps().getValue(ConfigurationPropertyKey.XA_TRANSACTION_MANAGER_TYPE);
// 创建事务上下文TransactionContexts//后面学习中细讲,本文不涉及
transactionContexts = createTransactionContexts(metaDataContexts.getDefaultMetaData().getResource().getDatabaseType(), dataSourceMap, xaTransactionMangerType);
}
MetaDataContextsBuilder.build()构建MetaDataContexts
org.apache.shardingsphere.infra.context.metadata.MetaDataContextsBuilder.build方法
/**
* Build meta data contexts.
*
* @exception SQLException SQL exception
* @return meta data contexts
*/
public StandardMetaDataContexts build() throws SQLException {
Map<String, ShardingSphereMetaData> mataDataMap = new HashMap<>(schemaRuleConfigs.size(), 1);
for (String each : schemaRuleConfigs.keySet()) {
// buildMetaData构造核心的元数据对象ShardingSphereMetaData,加入到集合中
mataDataMap.put(each, buildMetaData(each));
}
// 返回标准元数据上下文
return new StandardMetaDataContexts(mataDataMap, buildGlobalSchemaMetaData(mataDataMap), executorEngine, props);
}
ShardingSphere元数据
ShardingSphere元数据存储以下数据:
1数据源: 元数据资源,
2 规则配置
3 数据库元数据信息,表,字段,索引
//
@RequiredArgsConstructor
@Getter
public final class ShardingSphereMetaData {
private final String name;
// 数据源 元数据资源
private final ShardingSphereResource resource;
// 配置规则元数据: 原始配置RuleConfiguration集合 =》 配置解析后的ShardingSphereRule集合
private final ShardingSphereRuleMetaData ruleMetaData;
// 数据库表元数据: 字段元数据及索引元数据
private final ShardingSphereSchema schema;
/**
* Judge whether is completed.
*
* @return is completed or not
*/
public boolean isComplete() {
return !ruleMetaData.getRules().isEmpty() && !resource.getDataSources().isEmpty();
}
}
再看如何构建ShardingSphere元数据,buildMetaData方法
private ShardingSphereMetaData buildMetaData(final String schemaName) throws SQLException {
Map<String, DataSource> dataSourceMap = dataSources.get(schemaName);
Collection<RuleConfiguration> ruleConfigs = schemaRuleConfigs.get(schemaName);
//确定数据库类型
DatabaseType databaseType = DatabaseTypeRecognizer.getDatabaseType(dataSourceMap.values());
//a) 通过用户配置的RuleConfiguration 解析出 ShardingSphereRule
Collection<ShardingSphereRule> rules = ShardingSphereRulesBuilder.buildSchemaRules(schemaName, ruleConfigs, databaseType, dataSourceMap);
// 构造规则元数据对象ShardingSphereRuleMetaData
ShardingSphereRuleMetaData ruleMetaData = new ShardingSphereRuleMetaData(ruleConfigs, rules);
//构造ShardingSphere元数据对象ShardingSphereMetaData
//b) 构建数据源的元数据对象 c) 构建数据库的元数据
return new ShardingSphereMetaData(schemaName, buildResource(databaseType, dataSourceMap), ruleMetaData, buildSchema(databaseType, dataSourceMap, rules));
}
a) ShardingSphere规则元数据
@RequiredArgsConstructor
@Getter
public final class ShardingSphereRuleMetaData {
// 用户配置的规则对象集合
private final Collection<RuleConfiguration> configurations;
// 解析后的规则集合
private final Collection<ShardingSphereRule> rules;
}
RuleConfiguration直接对应了用户的yaml, properties等配置
ShardingSphereRule
buildSchemaRules()
public static Collection<ShardingSphereRule> buildSchemaRules(final String schemaName, final Collection<RuleConfiguration> schemaRuleConfigurations,
final DatabaseType databaseType, final Map<String, DataSource> dataSourceMap) {
Map<RuleConfiguration, SchemaRuleBuilder> builders = OrderedSPIRegistry.getRegisteredServices(schemaRuleConfigurations, SchemaRuleBuilder.class);
appendDefaultKernelSchemaRuleConfigurationBuilder(builders);
// SchemaRuleBuilder. build() 构建ShardingSphereRule
return builders.entrySet().stream().map(entry -> entry.getValue().build(schemaName, dataSourceMap, databaseType, entry.getKey())).collect(Collectors.toList());
}
SchemaRuleBuilder作为一个规则解析扩展点
b) 再看buildResource,即构建数据源的元数据对象
private ShardingSphereResource buildResource(final DatabaseType databaseType, final Map<String, DataSource> dataSourceMap) throws SQLException {
//构建数据源的元数据对象
DataSourcesMetaData dataSourceMetas = new DataSourcesMetaData(databaseType, getDatabaseAccessConfigurationMap(dataSourceMap));
//缓存下数据源的各类属性 链接,用户名,驱动,各类版本等等的原始信息
CachedDatabaseMetaData cachedDatabaseMetaData = createCachedDatabaseMetaData(dataSourceMap).orElse(null);
return new ShardingSphereResource(dataSourceMap, dataSourceMetas, cachedDatabaseMetaData, databaseType);
}
c) 再看如何构建数据库元数据, buildSchema()方法
加载数据库的元数据,但是各个方言版本的元数据不太一样,例如mysql的在information_schema中,针对对不同方言,可以通过DialectTableMetaDataLoader的spi扩展定制各类方言版本的数据库元数据加载。
- 数据库元数据
public final class ShardingSphereSchema {
// 表元数据
private final Map<String, TableMetaData> tables;
...
}
- 数据库表元数据
// 表元数据
public final class TableMetaData {
// 组合 列元数据
private final Map<String, ColumnMetaData> columns;
// 组合 索引元数据
private final Map<String, IndexMetaData> indexes;
...
}
列元数据
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public final class ColumnMetaData {
// 列名
private final String name;
// 数据类型
private final int dataType;
// 是否主键
private final boolean primaryKey;
// 是否自动生成
private final boolean generated;
// 是否大小敏感
private final boolean caseSensitive;
}
public final class IndexMetaData {
// 索引字段名
private final String name;
}
buildSchema()方法
private ShardingSphereSchema buildSchema(final DatabaseType databaseType, final Map<String, DataSource> dataSourceMap, final Collection<ShardingSphereRule> rules) throws SQLException {
return SchemaBuilder.build(new SchemaBuilderMaterials(databaseType, dataSourceMap, rules, props));
}
public static ShardingSphereSchema build(final SchemaBuilderMaterials materials) throws SQLException {
ShardingSphereSchema result = new ShardingSphereSchema();
// 有规则配置的表的处理 可以通过 RuleBasedTableMetaDataBuilder 的spi扩展解析
addRuleConfiguredTables(materials, result);
// 通过找对对应方言数据库的加载器DialectTableMetaDataLoader加载
appendRemainTables(materials, result);
return result;
}
总结
整个加载过程中使用不少设计模式,构造器模式,工厂模式,装饰器,组合等等,spi扩展点也预留不少,仔细研读收获颇丰。
目前的解读只是从大的主线来理解,当然中间还有很多细节需要后续补充。
debug过程中容易走丢,实时记录下路线是个不错的方式: