1. 首页
  2. sharding-jdbc源码分析

2. sharding-jdbc源码之Configuration

阿飞Javaer,转载请注明原创出处,谢谢!

上篇文章sharding-jdbc源码之数据源介绍了通过Java硬编码创建ShardingDataSource。这篇文章通过分析sharding-jdbc-config-parent模块,学习如何通过YAML配置或者spring配置创建ShardingDataSourcesharding-jdbc-config-parent模块包含了三个子模块,关系如下图所示:
sharding-jdbc-config-parent
|__sharding-jdbc-config-common
|__sharding-jdbc-config-spring
|__sharding-jdbc-config-yaml

无论是yaml方式还是spring方式配置ShardingDataSource,最终都会转化为sharding-jdbc-config-common中定义的对象;接下来对两种方式进行源码分析:

YAML配置

可以通过sharding-jdbc-example-config-yaml模块中YamlWithAssignedDataSourceMain.java进行debug;通过YamlWithAssignedDataSourceMain.java源码可知,yaml方式配置数据库的核心源码在YamlShardingDataSource中;

  public final class YamlWithAssignedDataSourceMain {

        public static void main(final String[] args) throws Exception {
            YamlShardingDataSource dataSource =  new YamlShardingDataSource(
                new File(YamlWithAssignedDataSourceMain.class.getResource("/META-INF/withAssignedDataSource.yaml").getFile()));
            ... ...
        }
    }

说明:withAssignedDataSource.yaml的内容请自行查看源码;

com.dangdang.ddframe.rdb.sharding.config.yaml.api.YamlShardingDataSource.java位于sharding-jdbc-config-yaml模块中,核心源码如下:

  public class YamlShardingDataSource extends ShardingDataSource {

        // 通过yaml文件配置数据源的方式
        public YamlShardingDataSource(final File yamlFile) throws IOException, SQLException {
            // unmarshal(yamlFile)方法是解析yaml文件的核心源码,其作用是将yaml文件解释为YamlConfig(父类是ShardingRuleConfig)
            super(new ShardingRuleBuilder(yamlFile.getName(), unmarshal(yamlFile)).build(), unmarshal(yamlFile).getProps());
        }

        ... ...

        private static YamlConfig unmarshal(final File yamlFile) throws IOException {
            try (
                    FileInputStream fileInputStream = new FileInputStream(yamlFile);
                    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8")
            ) {
                // yaml解释依赖第三方组件:org.yaml.snakeyaml; config-all.yaml内容解释成ShardingRuleConfig
                return new Yaml(new Constructor(YamlConfig.class)).loadAs(inputStreamReader, YamlConfig.class);
            }
        }

        private static YamlConfig unmarshal(final byte[] yamlByteArray) throws IOException {
            return new Yaml(new Constructor(YamlConfig.class)).loadAs(new ByteArrayInputStream(yamlByteArray), YamlConfig.class);

        }
    }

通过这段源码可知,接下来就会调用ShardingDataSource的构造方法,因为YamlShardingDataSource构造方法中调用了super(),而且YamlShardingDataSource继承自ShardingDataSource;

spring配置

可以通过sharding-jdbc-example-config-spring模块中SpringNamespaceWithAssignedDataSourceMain.java进行debug;其源码就是加载applicationContextWithAssignedDataSource.xml文件,该文件中<rdb>节点即sharding-jdbc定义节点部分的内容如下:

  <rdb:strategy id="databaseStrategy" sharding-columns="user_id" algorithm-expression="dbtbl_${user_id.longValue() % 2}"/>
    <rdb:strategy id="orderTableStrategy" sharding-columns="order_id" algorithm-expression="t_order_${order_id.longValue() % 4}"/>
    <rdb:strategy id="orderItemTableStrategy" sharding-columns="order_id" algorithm-class="com.dangdang.ddframe.rdb.sharding.example.config.spring.algorithm.SingleKeyModuloTableShardingAlgorithm"/>
    <rdb:data-source id="shardingDataSource">
        <rdb:sharding-rule data-sources="dbtbl_0,dbtbl_1,dbtbl_config">
            <rdb:table-rules>
                <rdb:table-rule logic-table="t_config" actual-tables="dbtbl_config.t_config"/>
                <rdb:table-rule logic-table="t_order" actual-tables="dbtbl_${0..1}.t_order_${0..3}" 
                database-strategy="databaseStrategy" table-strategy="orderTableStrategy"/>
                <rdb:table-rule logic-table="t_order_item" actual-tables="
                dbtbl_${0..1}.t_order_item_0,
                dbtbl_${0..1}.t_order_item_1,
                dbtbl_${0..1}.t_order_item_2,
                dbtbl_${0..1}.t_order_item_3" 
                database-strategy="databaseStrategy" table-strategy="orderItemTableStrategy"/>
            </rdb:table-rules>
            <rdb:default-database-strategy sharding-columns="none" algorithm-class="com.dangdang.ddframe.rdb.sharding.api.strategy.database.NoneDatabaseShardingAlgorithm"/>
            <rdb:default-table-strategy sharding-columns="none" algorithm-class="com.dangdang.ddframe.rdb.sharding.api.strategy.table.NoneTableShardingAlgorithm"/>
        </rdb:sharding-rule>
    </rdb:data-source>

配置文件基于inline表达式,部分内容解读如下:

  • 逻辑表表名为t_order,其实际表是dbtbl_{0..1}.t_order_{0..3}
  • t_order表的分表策略,通过table-strategy指定,即orderTableStrategy–根据order_id列的值对4取模;
  • t_order_item分表策略,通过table-strategy指定,即orderItemTableStrategy–实现算法就是根据根据order_id列对4取模;具体实现请参考SingleKeyModuloTableShardingAlgorithm;
  • 数据库的分库策略,通过database-strategy指定,即databaseStrategy–根据user_id列的值对2取模;
  • 默认数据库和表的分库分表策略:不需要根据任何列水平切分(sharding-columns="none");

通过sharding-jdbc-config-spring模块中spring.handlers里的配置http\://www.dangdang.com/schema/ddframe/rdb=com.dangdang.ddframe.rdb.sharding.spring.namespace.handler.ShardingJdbcNamespaceHandler
可知,spring.xml中的&lt;rdb>节点由ShardingJdbcNamespaceHandler进行解析,核心源码如下:

  public final class ShardingJdbcNamespaceHandler extends NamespaceHandlerSupport {

        @Override
        public void init() {
            // 注册<rdb:strategy>节点的解析器为ShardingJdbcStrategyBeanDefinitionParser
            registerBeanDefinitionParser("strategy", new ShardingJdbcStrategyBeanDefinitionParser());
            // 注册<rdb:data-source>节点的解析器为ShardingJdbcDataSourceBeanDefinitionParser
            registerBeanDefinitionParser("data-source", new ShardingJdbcDataSourceBeanDefinitionParser());
            // 注册<rdb:master-slave-data-source>节点的解析器为MasterSlaveDataSourceBeanDefinitionParser
            registerBeanDefinitionParser("master-slave-data-source", new MasterSlaveDataSourceBeanDefinitionParser());
        }
    }

spring.xml中data-source节点剖析:
根据上面ShardingJdbcNamespaceHandler里的源码可知,&lt;rdb:data-source>节点由ShardingJdbcDataSourceBeanDefinitionParser解析,核心源码如下;

  // 自定义Parser一定要实现org.springframework.beans.factory.xml.AbstractBeanDefinitionParser才能作为spring.xml中节点中的解析器,这是spring的约定;
    public class ShardingJdbcDataSourceBeanDefinitionParser extends AbstractBeanDefinitionParser {

        @Override
        // 这是解析入口,这时element是spring.xml中`<rdb:data-source>`节点;
        protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext parserContext) {
            // 准备把`<rdb:data-source>`节点中数据解析成为SpringShardingDataSource
            BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(SpringShardingDataSource.class);
            // 解析成SpringShardingDataSource且增加两个构造方法中属性的值(由后面SpringShardingDataSource.java定义可知,构造方法需要两个参数:一个是ShardingRuleConfig类型,一个是Properties类型)
            factory.addConstructorArgValue(parseShardingRuleConfig(element, parserContext));
            factory.addConstructorArgValue(parseProperties(element, parserContext));
            factory.setDestroyMethodName("close");
            return factory.getBeanDefinition();
        }

        // 这是解析SpringShardingDataSource构造方法中ShardingRuleConfig类型参数的值
        private BeanDefinition parseShardingRuleConfig(final Element element, final ParserContext parserContext) {
            // 先获取<rdb:sharding-rule>节点
            Element shardingRuleElement = DomUtils.getChildElementByTagName(element, ShardingJdbcDataSourceBeanDefinitionParserTag.SHARDING_RULE_CONFIG_TAG);
            // 将这个节点内容解析成ShardingRuleConfig(参数后面的ShardingRuleConfig.java定义)
            BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ShardingRuleConfig.class);
            // ShardingRuleConfig中dataSource属性赋值
            factory.addPropertyValue("dataSource", parseDataSources(shardingRuleElement, parserContext));
            // ShardingRuleConfig中defaultDataSourceName属性赋值
            parseDefaultDataSource(factory, shardingRuleElement);
            // ShardingRuleConfig中tables属性赋值
            factory.addPropertyValue("tables", parseTableRulesConfig(shardingRuleElement));
            // ShardingRuleConfig中bindingTables属性赋值
            factory.addPropertyValue("bindingTables", parseBindingTablesConfig(shardingRuleElement));
            // ShardingRuleConfig中defaultDatabaseStrategy属性赋值
            factory.addPropertyValue("defaultDatabaseStrategy", parseDefaultDatabaseStrategyConfig(shardingRuleElement));
            // ShardingRuleConfig中defaultTableStrategy属性赋值
            factory.addPropertyValue("defaultTableStrategy", parseDefaultTableStrategyConfig(shardingRuleElement));
            // ShardingRuleConfig中keyGeneratorClass属性赋值
            parseKeyGenerator(factory, shardingRuleElement);
            return factory.getBeanDefinition();
        }

SpringShardingDataSource.java定义:

  public class SpringShardingDataSource extends ShardingDataSource {
        // parseInternal()中解析完spring.xml中的<rdb:data-source>节点后,调用这个构造方法
        public SpringShardingDataSource(final ShardingRuleConfig shardingRuleConfig, final Properties props) throws SQLException {
            super(new ShardingRuleBuilder(shardingRuleConfig).build(), props);
        }
    }

ShardingDataSource剖析

无论是yaml配置还是spring.xml配置,最终都会调用ShardingDataSource里的构造方法,接下来对其进行分析;

  public class ShardingDataSource extends AbstractDataSourceAdapter implements AutoCloseable {
        public ShardingDataSource(final ShardingRule shardingRule, final Properties props) throws SQLException {
            super(shardingRule.getDataSourceRule().getDataSources());
            shardingProperties = new ShardingProperties(null == props ? new Properties() : props);
            // 默认值是CPU核心数
            int executorSize = shardingProperties.getValue(ShardingPropertiesConstant.EXECUTOR_SIZE);
            // ExecutorEngine的构造依赖于google-guava的MoreExecutors
            executorEngine = new ExecutorEngine(executorSize);
            // 是否有配置文件配置了sql_show
            boolean showSQL = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SHOW);
            shardingContext = new ShardingContext(shardingRule, getDatabaseType(), executorEngine, showSQL);
        }
        ...
    }

通过该构造方法的源码可知: 申明的数据源集合,例如spring.xml中&lt;rdb:sharding-rule data-sources=&quot;dbtbl_0,dbtbl_1,dbtbl_config&quot;> ,所有数据源必须是相同的数据库类型;要么全是MySQL,要么全是Oracle;否则抛出异常:Database type inconsistent with ‘%s’ and ‘%s’;其数据库类型根据connection.getMetaData().getDatabaseProductName()得到;

另外,通过这段源码可知,可配置的属性有sql_showexecutor.size,定义在ShardingPropertiesConstant.java中:

  1. 两个属性在spring.xml中的配置参考:
  <rdb:data-source id="shardingDataSource">
        <rdb:sharding-rule  data-sources="dbtbl_0,dbtbl_1,dbtbl_config">
            ... ...
        </rdb:sharding-rule>
        <rdb:props>
            <prop key="sql.show">true</prop>
            <prop key="executor.size">2</prop>
        </rdb:props>
    </rdb:data-source>

  1. 两个属性在yaml文件中的配置参考:
  props:
      sql.show: false
      executor.size: 4

附ShardingRuleConfig.java定义:

  @Getter
    @Setter
    public class ShardingRuleConfig {

        private Map<String, DataSource> dataSource = new HashMap<>();

        private String defaultDataSourceName;

        private Map<String, TableRuleConfig> tables = new HashMap<>();

        private List<BindingTableRuleConfig> bindingTables = new ArrayList<>();

        private StrategyConfig defaultDatabaseStrategy;

        private StrategyConfig defaultTableStrategy;

        private String keyGeneratorClass;
    }

Debug

以spring配置数据源的方式进行debug,Main方法为SpringNamespaceWithAssignedDataSourceMain.java,debug之前,需要执行sharding-jdbc-example-config-spring模块中的all_schema.sql脚本;

YAML解析&lombok实战

通过上面对sharding-jdbc源码的分析,发现sharding-jdbc支持yaml格式配置,且大量使用lombok简化源码,接下来简单实践yaml格式文件如何解析,以及lombok如何使用;

假设需要解析的yaml文件内容如下:

  rdb:
      oracle:
        username: OracleUse&1
        password: OrcUse*&1
        driverClassName: oracle.jdbc.OracleDriver
        url: jdbc:oracle:thin:@192.168.0.2:1521:xe
      mysql:
        username: MySQLUse&1
        password: MyUse*&1
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.0.1:3306/financials_rules?autoCommit=true

    nosql:
      mongodb:
        username: MongoUse&1
        password: MgoUse*&1
      redis:
        password: RdsUse*&1

    newsql:

解析yaml文件的核心代码如下:

  public class DataSourceTest {

        /**
         * 这个yaml文件要放在resources目录下
         */
        private static final String YAML_FILE_PATH = "datasource.yaml";

        public static void main(String[] args) throws Exception {
            System.out.println(JSON.toJSONString(
                    unmarshal(DataSourceTest.class.getClassLoader().getResourceAsStream(YAML_FILE_PATH))));
        }

        private static DataSourceConfig unmarshal(final InputStream is) throws IOException {
            try (
                    InputStreamReader inputStreamReader = new InputStreamReader(is, "UTF-8")
            ) {
                return new Yaml(new Constructor(DataSourceConfig.class)).loadAs(inputStreamReader, DataSourceConfig.class);
            }
        }

    }

DataSourceConfig.java源码如下:

  @Getter
    @Setter
    public class DataSourceConfig {

        private Map<String, DataSourceItemConfig> rdb;

        private Map<String, DataSourceItemConfig> nosql;

        private Map<String, DataSourceItemConfig> newsql;

    }

DataSourceItemConfig.java源码如下:

  @Getter
    @Setter
    public class DataSourceItemConfig {

        private String username;

        private String password;

        private String driverClassName;

        private String url;

    }

最终输出结果为:

  {
        "nosql": {
            "mongodb": {
                "password": "MgoUse*&1",
                "username": "MongoUse&1"
            },
            "redis": {
                "password": "RdsUse*&1"
            }
        },
        "rdb": {
            "oracle": {
                "driverClassName": "oracle.jdbc.OracleDriver",
                "password": "OrcUse*&1",
                "url": "jdbc:oracle:thin:@192.168.0.2:1521:xe",
                "username": "OracleUse&1"
            },
            "mysql": {
                "driverClassName": "com.mysql.jdbc.Driver",
                "password": "MyUse*&1",
                "url": "jdbc:mysql://192.168.0.1:3306/financials_rules?autoCommit=true",
                "username": "MySQLUse&1"
            }
        }
    }

YAML&lombok Maven坐标

  <dependency>
        <groupId>org.yaml</groupId>
        <artifactId>snakeyaml</artifactId>
        <version>1.16</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.4</version>
        <scope>provided</scope>
    </dependency>

作者:阿飞的博客

来源:https://www.jianshu.com/p/57918e2df0d0


看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「方志朋」,公众号后台回复「666」 免费领取我精心整理的进阶资源教程
  4. JS中文网,Javascriptc中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,是给开发者用的 Hacker News,技术文章由为你筛选出最优质的干货,其中包括:Android、iOS、前端、后端等方面的内容。目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。

    本文著作权归作者所有,如若转载,请注明出处

    转载请注明:文章转载自「 Java极客技术学习 」https://www.javajike.com

    标题:2. sharding-jdbc源码之Configuration

    链接:https://www.javajike.com/article/1862.html

« 4. sharding-jdbc源码之分布式ID
3. sharding-jdbc源码之路由&执行»

相关推荐

QR code