Sharding-JDBC系列
2、Sharding-JDBC分库分表之SpringBoot分片策略
3、Sharding-JDBC分库分表之SpringBoot主从配置
4、SpringBoot集成Sharding-JDBC-5.3.0分库分表
5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
8、【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理
9、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(一)
前言
在前一篇博文【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理-CSDN博客从源码角度分享了分片配置文件解析的原理,分片配置Yaml文件使用SnakeYaml进行解析,返回一个YamlRootConfiguration对象。本篇从源码的角度继续分享分片配置信息的原理。
YamlRootConfiguration
YamlRootConfiguration是整个配置文件的根节点配置信息,源码如下:
package org.apache.shardingsphere.infra.yaml.config.pojo;
/**
* Yaml分片配置的根配置
*/
@Getter
@Setter
public final class YamlRootConfiguration implements YamlConfiguration {
// 数据库名
private String databaseName;
/**
* Schema name.
*
* @deprecated Should use databaseName, schemaName will remove in next version.
*/
// 在新版本将会移除,同databaseName
@Deprecated
private String schemaName;
// 数据源信息,使用Map存储。key为逻辑数据源名称,value为对应数据源的配置信息(如url、username等)
private Map<String, Map<String, Object>> dataSources = new HashMap<>();
// 规则配置,Collection集合类型
private Collection<YamlRuleConfiguration> rules = new LinkedList<>();
// 模式配置
private YamlModeConfiguration mode;
// 属性配置
private Properties props = new Properties();
public String getDatabaseName() {
return Strings.isNullOrEmpty(databaseName) ? schemaName : databaseName;
}
}
在YamlRootConfiguration中定义的属性为配置的key,属性类型为对应值的信息。在分片配置文件中可以配置的根节点信息为:
databaseName: #数据库名,String类型
dataSources: #数据源,Map<String, Map<String, Object>>类型
rules: # 规则,Collection<YamlRuleConfiguration>类型
props: # 属性,Properties类型
mode: # 模式,YamlModeConfiguration类型
2.1 databaseName
数据库名称,默认为logic_db
2.2 dataSources
数据源集合,key为逻辑数据源名称,value为对应数据源的配置信息。
YamlRootConfiguration根配置对象是在YamlShardingSphereDataSourceFactory的createDateSource()方法中,通过YamlEngine.unmarshal()方法解析获得的。代码如下:
package org.apache.shardingsphere.driver.api.yaml;
public final class YamlShardingSphereDataSourceFactory {
// yaml中dataSources配置信息转换器
private static final YamlDataSourceConfigurationSwapper DATA_SOURCE_SWAPPER = new YamlDataSourceConfigurationSwapper();
/**
* 创建ShardingSphereDataSource对象
* @param yamlBytes yaml配置字符串
*/
public static DataSource createDataSource(final byte[] yamlBytes) throws SQLException, IOException {
// 解析yaml配置文件,获取YamlRootConfiguration
YamlRootConfiguration rootConfig = YamlEngine.unmarshal(yamlBytes, YamlRootConfiguration.class);
// 调用DATA_SOURCE_SWAPPER.swapToDataSources(rootConfig.getDataSources(),
// 将Yaml中配置的dataSources信息转换为对应的DataSources对象
return createDataSource(DATA_SOURCE_SWAPPER.swapToDataSources(rootConfig.getDataSources()), rootConfig);
}
// 省略其他
}
获取之后,执行YamlDataSourceConfigurationSwapper的swapToDataSources()方法,根据配置的dataSources信息创建DataSource对象。
2.2.1 YamlDataSourceConfigurationSwapper
YamlDataSourceConfigurationSwapper的相关源码如下:
package org.apache.shardingsphere.infra.yaml.config.swapper.resource;
/**
* Yaml的dataSource配置转换
*/
public final class YamlDataSourceConfigurationSwapper {
private static final String DATA_SOURCE_CLASS_NAME_KEY = "dataSourceClassName";
private static final String CUSTOM_POOL_PROPS_KEY = "customPoolProps";
/**
* 将Yaml中配置的dataSources信息转换为对应的DataSources对象
*/
public Map<String, DataSource> swapToDataSources(final Map<String, Map<String, Object>> yamlDataSources) {
return swapToDataSources(yamlDataSources, true);
}
/**
* 数据源配置信息转换(创建)对应的DataSource对象
* @param yamlDataSources
* @param cacheEnabled
* @return
*/
public Map<String, DataSource> swapToDataSources(final Map<String, Map<String, Object>> yamlDataSources, final boolean cacheEnabled) {
return DataSourcePoolCreator.create(yamlDataSources.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> swapToDataSourceProperties(entry.getValue()))), cacheEnabled);
}
/**
* yaml配置信息转换为DataSourceProperties对象
*/
public DataSourceProperties swapToDataSourceProperties(final Map<String, Object> yamlConfig) {
// 必现包含dataSourceClassName配置
Preconditions.checkState(yamlConfig.containsKey(DATA_SOURCE_CLASS_NAME_KEY), "%s can not be null.", DATA_SOURCE_CLASS_NAME_KEY);
// 拷贝yamlConfig,创建DataSourceProperties对象
return new DataSourceProperties(yamlConfig.get(DATA_SOURCE_CLASS_NAME_KEY).toString(), getProperties(yamlConfig));
}
/**
* 深度拷贝yamlConfig,移除dataSourceClassName配置。如果存在customPoolProps,
* 则将其配置信息添加到拷贝后的yamlConfig,移除customPoolProps
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private Map<String, Object> getProperties(final Map<String, Object> yamlConfig) {
Map<String, Object> result = new HashMap<>(yamlConfig);
// 移除dataSourceClassName配置信息
result.remove(DATA_SOURCE_CLASS_NAME_KEY);
// 如果包含customPoolProps自定义的池属性
if (null != yamlConfig.get(CUSTOM_POOL_PROPS_KEY)) {
// 将customPoolProps属性信息添加到根Map中
result.putAll((Map) yamlConfig.get(CUSTOM_POOL_PROPS_KEY));
}
// 移除customPoolProps配置
result.remove(CUSTOM_POOL_PROPS_KEY);
return result;
}
// 省略其他
}
在swapToDataSources()方法中,执行如下:
2.2.1.1)遍历配置的dataSources,调用swapToDataSourceProperties(final Map<String, Object> yamlConfig),将配置的信息转换为DataSourceProperties对象;
a)首先判断配置信息中是否有dataSourceClassName,如果没有,抛参数异常;
b)调用getProperties()方法,深度拷贝配置信息,并对customPoolProps的配置信息放到根Map中。即dataSources.xxx_ds.customPoolProps.aaa = bbb,等价于dataSources.xxx_ds.aaa = bbb;
c)创建一个DataSourceProperties对象;
2.2.1.2)调用DataSourcePoolCreator.create(final Map<String, DataSourceProperties> dataSourcePropsMap, final boolean cacheEnabled),创建DataSource对象;
2.2.2 DataSourceProperties
DataSourceProperties为数据源的属性信息。源码如下:
package org.apache.shardingsphere.infra.datasource.props;
/**
* DataSource的属性信息
*/
@Getter
public final class DataSourceProperties {
// dataSource的类名,如com.zaxxer.hikari.HikariDataSource、DruidDataSource等全类名
private final String dataSourceClassName;
// connection连接属性同义名。记录连接的属性及配置信息(如url、username等)
private final ConnectionPropertySynonyms connectionPropertySynonyms;
// 连接池属性的同义词。记录连接池的属性及配置信息(如maxPoolSize等)
private final PoolPropertySynonyms poolPropertySynonyms;
// 其他自定义的DataSource自定义属性
private final CustomDataSourceProperties customDataSourceProperties;
/**
* @param dataSourceClassName 如com.zaxxer.hikari.HikariDataSource
* @param props dataSource配置的属性信息
*/
public DataSourceProperties(final String dataSourceClassName, final Map<String, Object> props) {
this.dataSourceClassName = dataSourceClassName;
// 如HikariDataSourcePoolMetaData
Optional<DataSourcePoolMetaData> poolMetaData = TypedSPIRegistry.findRegisteredService(DataSourcePoolMetaData.class, dataSourceClassName);
// 获取同义词,即同义名。进行配置兼容性处理
Map<String, String> propertySynonyms = poolMetaData.isPresent() ? poolMetaData.get().getPropertySynonyms() : Collections.emptyMap();
connectionPropertySynonyms = new ConnectionPropertySynonyms(props, propertySynonyms);
poolPropertySynonyms = new PoolPropertySynonyms(props, propertySynonyms);
customDataSourceProperties = new CustomDataSourceProperties(
props, getStandardPropertyKeys(), poolMetaData.isPresent() ? poolMetaData.get().getTransientFieldNames() : Collections.emptyList(), propertySynonyms);
}
/**
* 获取标准属性key
* @return
*/
private Collection<String> getStandardPropertyKeys() {
Collection<String> result = new LinkedList<>(connectionPropertySynonyms.getStandardPropertyKeys());
result.addAll(poolPropertySynonyms.getStandardPropertyKeys());
return result;
}
/**
* 获取所有标准配置信息
* @return
*/
public Map<String, Object> getAllStandardProperties() {
Map<String, Object> result = new LinkedHashMap<>(
connectionPropertySynonyms.getStandardProperties().size() + poolPropertySynonyms.getStandardProperties().size() + customDataSourceProperties.getProperties().size(), 1);
result.putAll(connectionPropertySynonyms.getStandardProperties());
result.putAll(poolPropertySynonyms.getStandardProperties());
result.putAll(customDataSourceProperties.getProperties());
return result;
}
/**
* 获取所有本地属性信息
* @return
*/
public Map<String, Object> getAllLocalProperties() {
Map<String, Object> result = new LinkedHashMap<>(
connectionPropertySynonyms.getLocalProperties().size() + poolPropertySynonyms.getLocalProperties().size() + customDataSourceProperties.getProperties().size(), 1);
result.putAll(connectionPropertySynonyms.getLocalProperties());
result.putAll(poolPropertySynonyms.getLocalProperties());
result.putAll(customDataSourceProperties.getProperties());
return result;
}
// 省略其他
}
在DataSourceProperties构造方法中,根据配置的dataSourceClassName,使用Java SPI获取对应dataSource的DataSourcePoolMetaData元数据,根据配置的dataSourceClassName,对配置信息进行兼容性处理。
不同的DataSource处理方式不同,以com.zaxxer.hikari.HikariDataSource为例,对应的DataSourcePoolMetaData为HikariDataSourcePoolMetaData。
2.2.3 HikariDataSourcePoolMetaData
HikariDataSourcePoolMetaData的源码如下:
package org.apache.shardingsphere.infra.datasource.pool.metadata.type.hikari;
/**
* HikariDataSource池的元数据,创建默认的池信息
*/
public final class HikariDataSourcePoolMetaData implements DataSourcePoolMetaData {
//默认属性
private static final Map<String, Object> DEFAULT_PROPS = new HashMap<>(6, 1);
// 非法的属性
private static final Map<String, Object> INVALID_PROPS = new HashMap<>(2, 1);
// 同名属性
private static final Map<String, String> PROP_SYNONYMS = new HashMap<>(6, 1);
// 忽略的属性名称
private static final Collection<String> TRANSIENT_FIELD_NAMES = new LinkedList<>();
static {
buildDefaultProperties();
buildInvalidProperties();
buildPropertySynonyms();
buildTransientFieldNames();
}
private static void buildDefaultProperties() {
DEFAULT_PROPS.put("connectionTimeout", 30 * 1000L);
DEFAULT_PROPS.put("idleTimeout", 60 * 1000L);
DEFAULT_PROPS.put("maxLifetime", 30 * 70 * 1000L);
DEFAULT_PROPS.put("maximumPoolSize", 50);
DEFAULT_PROPS.put("minimumIdle", 1);
DEFAULT_PROPS.put("readOnly", false);
DEFAULT_PROPS.put("keepaliveTime", 0);
}
private static void buildInvalidProperties() {
INVALID_PROPS.put("minimumIdle", -1);
INVALID_PROPS.put("maximumPoolSize", -1);
}
/**
* 属性的同义词,如url和jdbcUrl属于同一个配置
*/
private static void buildPropertySynonyms() {
PROP_SYNONYMS.put("url", "jdbcUrl");
PROP_SYNONYMS.put("connectionTimeoutMilliseconds", "connectionTimeout");
PROP_SYNONYMS.put("idleTimeoutMilliseconds", "idleTimeout");
PROP_SYNONYMS.put("maxLifetimeMilliseconds", "maxLifetime");
PROP_SYNONYMS.put("maxPoolSize", "maximumPoolSize");
PROP_SYNONYMS.put("minPoolSize", "minimumIdle");
}
private static void buildTransientFieldNames() {
TRANSIENT_FIELD_NAMES.add("running");
TRANSIENT_FIELD_NAMES.add("poolName");
TRANSIENT_FIELD_NAMES.add("closed");
}
@Override
public Map<String, Object> getDefaultProperties() {
return DEFAULT_PROPS;
}
@Override
public Map<String, Object> getInvalidProperties() {
return INVALID_PROPS;
}
@Override
public Map<String, String> getPropertySynonyms() {
return PROP_SYNONYMS;
}
@Override
public Collection<String> getTransientFieldNames() {
return TRANSIENT_FIELD_NAMES;
}
/**
* Hikari数据源池字段的元数据。包括username、password、jdbcUrl、jdbcUrlProperties四个字段名称
*/
@Override
public HikariDataSourcePoolFieldMetaData getFieldMetaData() {
return new HikariDataSourcePoolFieldMetaData();
}
/**
* 返回属性校验器
*/
@Override
public DataSourcePoolPropertiesValidator getDataSourcePoolPropertiesValidator() {
return new HikariDataSourcePoolPropertiesValidator();
}
@Override
public String getType() {
return "com.zaxxer.hikari.HikariDataSource";
}
}
在HikariDataSourcePoolMetaData元数据中,除了定义同义名以外,还做了默认项配置。该默认配置项在上面的2.2.1.2)中创建DataSource对象时,会通过反射的方式,赋值到对应的DataSource对象上。
2.2.4 DataSourcePoolCreator
在上面的2.2.1.2)调用DataSourcePoolCreator.create(final Map<String, DataSourceProperties> dataSourcePropsMap, final boolean cacheEnabled),创建DataSource对象。
DataSourcePoolCreator的源码如下:
package org.apache.shardingsphere.infra.datasource.pool.creator;
/**
* DataSource池的创建器。通过配置的信息,创建对应的DataSource(如HikariDataSource),
* 通过反射机制,讲配置的信息赋值给DataSource
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DataSourcePoolCreator {
/**
* 根据DataSource的属性配置信息,创建对应的DataSource对象
*/
public static Map<String, DataSource> create(final Map<String, DataSourceProperties> dataSourcePropsMap) {
return create(dataSourcePropsMap, true);
}
/**
* 遍历DataSourceProperties,创建对应的DataSource对象
*/
public static Map<String, DataSource> create(final Map<String, DataSourceProperties> dataSourcePropsMap, final boolean cacheEnabled) {
return dataSourcePropsMap.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> create(entry.getKey(), entry.getValue(), cacheEnabled), (oldValue, currentValue) -> oldValue,
LinkedHashMap::new));
}
/**
* 创建DataSource
*/
public static DataSource create(final DataSourceProperties dataSourceProps) {
// 通过Class.forName()获得构造器,创建一个数据源对象。如HikariDataSource对象
DataSource result = createDataSource(dataSourceProps.getDataSourceClassName());
// 获取对应DataSource的池元数据
Optional<DataSourcePoolMetaData> poolMetaData = TypedSPIRegistry.findRegisteredService(DataSourcePoolMetaData.class, dataSourceProps.getDataSourceClassName());
DataSourceReflection dataSourceReflection = new DataSourceReflection(result);
// 通过反射,把配置中的值设置到DataSource。通过反射,代码更加简洁且支持性更好、更灵活
if (poolMetaData.isPresent()) {
setDefaultFields(dataSourceReflection, poolMetaData.get());
setConfiguredFields(dataSourceProps, dataSourceReflection, poolMetaData.get());
appendJdbcUrlProperties(dataSourceProps.getCustomDataSourceProperties(), result, poolMetaData.get());
dataSourceReflection.addDefaultDataSourceProperties();
} else {
setConfiguredFields(dataSourceProps, dataSourceReflection);
}
return result;
}
/**
* Create data source.
*
* @param dataSourceName data source name
* @param dataSourceProps data source properties
* @param cacheEnabled cache enabled
* @return created data source
*/
public static DataSource create(final String dataSourceName, final DataSourceProperties dataSourceProps, final boolean cacheEnabled) {
// 创建DataSource,并赋值
DataSource result = create(dataSourceProps);
if (cacheEnabled && !GlobalDataSourceRegistry.getInstance().getCachedDataSourceDataSources().containsKey(dataSourceName)) {
// 加入到缓存
GlobalDataSourceRegistry.getInstance().getCachedDataSourceDataSources().put(dataSourceName, result);
}
return result;
}
/**
* 通过Class.forName()获得构造器,创建一个数据源对象
*/
@SneakyThrows(ReflectiveOperationException.class)
private static DataSource createDataSource(final String dataSourceClassName) {
return (DataSource) Class.forName(dataSourceClassName).getConstructor().newInstance();
}
/**
* 使用反射,添加配置的dataSourceClassName对应的默认配置信息
*/
private static void setDefaultFields(final DataSourceReflection dataSourceReflection, final DataSourcePoolMetaData poolMetaData) {
for (Entry<String, Object> entry : poolMetaData.getDefaultProperties().entrySet()) {
dataSourceReflection.setField(entry.getKey(), entry.getValue());
}
}
/**
* 通过反射,设置所有本地属性信息
* @param dataSourceProps
* @param dataSourceReflection
*/
private static void setConfiguredFields(final DataSourceProperties dataSourceProps, final DataSourceReflection dataSourceReflection) {
for (Entry<String, Object> entry : dataSourceProps.getAllLocalProperties().entrySet()) {
dataSourceReflection.setField(entry.getKey(), entry.getValue());
}
}
/**
* 使用反射,添加配置的属性
*/
private static void setConfiguredFields(final DataSourceProperties dataSourceProps, final DataSourceReflection dataSourceReflection, final DataSourcePoolMetaData poolMetaData) {
for (Entry<String, Object> entry : dataSourceProps.getAllLocalProperties().entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
if (isValidProperty(fieldName, fieldValue, poolMetaData) && !fieldName.equals(poolMetaData.getFieldMetaData().getJdbcUrlPropertiesFieldName())) {
dataSourceReflection.setField(fieldName, fieldValue);
}
}
}
private static boolean isValidProperty(final String key, final Object value, final DataSourcePoolMetaData poolMetaData) {
return !poolMetaData.getInvalidProperties().containsKey(key) || null == value || !value.equals(poolMetaData.getInvalidProperties().get(key));
}
/**
* 附加Jdbc Url属性
*/
@SuppressWarnings("unchecked")
private static void appendJdbcUrlProperties(final CustomDataSourceProperties customDataSourceProps, final DataSource targetDataSource, final DataSourcePoolMetaData poolMetaData) {
// 获取jdbc url属性名,如HikariDataSource为dataSourceProperties
String jdbcUrlPropertiesFieldName = poolMetaData.getFieldMetaData().getJdbcUrlPropertiesFieldName();
if (null != jdbcUrlPropertiesFieldName && customDataSourceProps.getProperties().containsKey(jdbcUrlPropertiesFieldName)) {
Map<String, Object> jdbcUrlProps = (Map<String, Object>) customDataSourceProps.getProperties().get(jdbcUrlPropertiesFieldName);
// 通过反射,获取DataSource池字段的元数据
DataSourcePoolMetaDataReflection dataSourcePoolMetaDataReflection = new DataSourcePoolMetaDataReflection(targetDataSource, poolMetaData.getFieldMetaData());
// 设置
for (Entry<String, Object> entry : jdbcUrlProps.entrySet()) {
dataSourcePoolMetaDataReflection.getJdbcConnectionProperties().ifPresent(optional -> optional.setProperty(entry.getKey(), entry.getValue().toString()));
}
}
}
}
通过配置的数据源信息创建DataSource对象的主要流程如下:
1)遍历DataSourceProperties;
2)根据DataSourceProperties对象,通过Class.forName()获得构造器,创建对应dataSourceClassName的数据源DataSource对象;
3)通过Java SPI获取对应数据源的DataSourcePoolMetaData元数据对象;
4)通过反射,获取DataSource对象的设置方法;
5)将DataSourceProperties中的配置信息通过反射设置到DataSource对象,包括DataSourcePoolMetaData元数据对象中的默认配置信息等;
小结
限于篇幅,本篇先分享到这里。以下做一个小结:
1)分片配置文件通过SnakeYaml解析成YamlRootConfiguration对象;
分片配置文件中可以配置的根key为:
databaseName: #数据库名,String类型
dataSources: #数据源,Map<String, Map<String, Object>>类型
rules: # 规则,Collection<YamlRuleConfiguration>类型
props: # 属性,Properties类型
mode: # 模式,YamlModeConfiguration类型
2)dataSources数据源配置,可配置多个;
2.1)dataSources必现配置dataSourceClassName,根据不同的dataSourceClassName,可以设置对应属性配置。默认有:com.mchange.v2.c3p0.ComboPooledDataSource、
com.zaxxer.hikari.HikariDataSource等;2.2)对于url、username、password等基础配置项,不同的dataSourceClass有对应的兼容性配置项;
2.3)不同的dataSourceClass添加了不同的默认配置项;
具体的配置项可参考:C3P0DataSourcePoolMetaData、HikariDataSourcePoolMetaData等
3)通过配置的dataSources创建对应的DataSource
3.1)根据配置的dataSources,每个dataSource生成一个DataSourceProperties。在DataSourceProperties中,处理了关键配置项的兼容性处理(如url、username等配置项);
3.2)根据配置的dataSourceClassName,生成对应的DataSource对象;
3.3)根据反射,将配置的信息赋值到DataSource对象中。配置的信息包括:yaml中的配置项、对应DataSource的DataSourcePoolMetaData中的默认配置;
以上为本篇分享的全部内容。
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。