DBLE 2.17.08.1与MyCat 1.6.5的启动过程(4)——加载配置文件rule.xml

rule.xml和schema.xml的加载概况

rule.xml和schema.xml的加载入口虽然都在com.actiontech.dble.config.loader.xml.XMLSchemaLoader(null, null, lowerCaseNames),但是,实际上解析加载rule.xml的类是com.actiontech.dble.config.loader.xml.XMLRuleLoader,所以XMLSchemaLoader这个类依赖XMLRuleLoader类:

XMLSchemaLoader在构造函数中实例化了一个即用即弃的XMLRuleLoader对象,用来加载rule.xml,在调用XMLRuleLoader.getTableRules()继承到了它的成果后,就不再使用这个对象,转向XMLSchemaLoader.load()来加载schema.xml。

rule.xml的加载过程

使用ConfigUtil.getDocument()将rule.xml加载入内存后,先用loadFunctions()对用户定义的算法(<function>的具体内容),再用loadTableRules()读取算法与表名的关联关系(<tableRule>的具体内容)。

在描述分片算法是如何跟逻辑表挂钩这个事情上,DBLE继承了MyCat的做法。两者都使用了遵从OOP理念的逐层抽象方法(我认为在这里反而是过于复杂化的):

  1. 最具体的概念是具体的逻辑分片函数类,例如内置哈希分片函数com.actiontech.dble.route.function.PartitionByLong,它在<function class="class_name">中指定;
  2. 这个类的一个实例化对象,必然会有它自己特殊的对象属性值(在<function>里的<property>中指定),这就构成了第一层的抽象概念functionalgorithm(这层抽象的称呼不太稳定,我觉得应该是项目管理不善导致的)
  3. functionalgorithm的基础上,加上用作分片索引的逻辑表的列名,就进一步抽象成了tableRule,可以供逻辑表使用了。

第一次抽象,生成function(algorithm)–loadFunctions()

工作过程与读取server.xml的时候一样,大致是:

  1. 从rule.xml中逐个找出<function>标签。

  2. 根据<function>标签的class属性,实例化指定分片函数的对象

  3. 根据<function>标签的name属性,设置分片函数对象的名称

  4. 执行分片函数对象自身的初始化方法,并登记到XMLRuleLoader中(加入到内部Map集合中)

private void loadFunctions(Element root) throws ClassNotFoundException,
        InstantiationException, IllegalAccessException,
        InvocationTargetException {

    // 提取rule.xml中所有的<function>标签
    NodeList list = root.getElementsByTagName("function");

    // 逐个<function>标签进行处理
    for (int i = 0, n = list.getLength(); i < n; ++i) {
        Node node = list.item(i);
        if (node instanceof Element) {
            Element e = (Element) node;

            // 根据<function>标签的name属性,确定该function的名称
            String name = e.getAttribute("name");

            // 基于name属性检查是否已经加载过该分片函数
            if (functions.containsKey(name)) {
                throw new ConfigException("rule function " + name + " duplicated!");
            }

            // 根据<function>标签的class属性,通过java反射创建分片函数类的对象
            String clazz = e.getAttribute("class");
            //reflection
            AbstractPartitionAlgorithm function = createFunction(name, clazz);

            function.setName(name);

            // 通过java反射设置分片函数对象的属性
            ParameterMapping.mapping(function, ConfigUtil.loadElements(e));

            // 执行分片函数对象的初始化方法
            function.init();

            // 注册分片函数对象到XMLRuleLoader的内部集合中
            functions.put(name, function);
        }
    }
}

其中,需要特别说明的是createFunction()这个方法。在这个方法中,DBLE保留了MyCat的传统,让用户通过完全限定类名指定分片函数;此外,还对内置的6种分片算法提供了简化名来简化<function>标签的配置。例如,内置哈希函数可以通过<function class="hash">来指定,而不用大费周章地写<function class="com.actiontech.dble.route.function.PartitionByLong">了。

简化名完全限定类名
hashcom.actiontech.dble.route.function.PartitionByLong
stringhashcom.actiontech.dble.route.function.PartitionByString
enumcom.actiontech.dble.route.function.PartitionByFileMap
numberrangecom.actiontech.dble.route.function.AutoPartitionByLong
patternrangecom.actiontech.dble.route.function.PartitionByPattern
datecom.actiontech.dble.route.function.PartitionByDate
private AbstractPartitionAlgorithm createFunction(String name, String clazz)
        throws ClassNotFoundException, InstantiationException,
        IllegalAccessException, InvocationTargetException {

    String lowerClass = clazz.toLowerCase();
    switch (lowerClass) {
        // 等效于<function class="com.actiontech.dble.route.function.PartitionByLong">
        case "hash":
            return new PartitionByLong();
        // 等效于<function class="com.actiontech.dble.route.function.PartitionByString">
        case "stringhash":
            return new PartitionByString();
        // 等效于<function class="com.actiontech.dble.route.function.PartitionByFileMap">
        case "enum":
            return new PartitionByFileMap();
        // 等效于<function class="com.actiontech.dble.route.function.AutoPartitionByLong">
        case "numberrange":
            return new AutoPartitionByLong();
        // 等效于<function class="com.actiontech.dble.route.function.PartitionByPattern">
        case "patternrange":
            return new PartitionByPattern();
        // 等效于<function class="com.actiontech.dble.route.function.PartitionByDate">
        case "date":
            return new PartitionByDate();
        default:
            Class<?> clz = Class.forName(clazz);
            //all function must be extend from AbstractPartitionAlgorithm
            if (!AbstractPartitionAlgorithm.class.isAssignableFrom(clz)) {
                throw new IllegalArgumentException("rule function must implements " +
                        AbstractPartitionAlgorithm.class.getName() + ", name=" + name);
            }
            return (AbstractPartitionAlgorithm) clz.newInstance();
    }

}

第二次抽象,生成tableRule–loadTableRules()

在第二次抽象后,生成的是com.actiontech.dble.config.model.rule.RuleConfig对象的Map集合,并存储在XMLRuleLoader中,供XMLSchemaLoader在后面加载schema.xml时使用。

工作过程也非常单纯,大致如下:

  1. 从rule.xml中逐个找出<tableRule>标签。

  2. 调用loadRule(),根据<tableRule>标签的<rule><columns><algorithm>,创建一个RuleConfig对象

  3. 根据<tableRule>标签的name属性,以此名称为key登记刚才新创建的RuleConfig对象到XMLRuleLoader中(加入到内部Map集合中)

private void loadTableRules(Element root) throws SQLSyntaxErrorException {

    // 提取rule.xml中所有的<tableRule>标签
    NodeList list = root.getElementsByTagName("tableRule");

    // 逐个<tableRule>标签进行处理
    for (int i = 0, n = list.getLength(); i < n; ++i) {
        Node node = list.item(i);
        if (node instanceof Element) {

            Element e = (Element) node;

            // 根据<tableRule>标签的name属性,确定该tableRule的外部命名,
            // RuleConfig本身是没有name或者id之类的称呼属性
            String name = e.getAttribute("name");

            // <tableRule>标签的name属性不能为空,也不能互相重复
            if (StringUtil.isEmpty(name)) {
                throw new ConfigException("name is null or empty");
            }
            if (tableRules.containsKey(name)) {
                throw new ConfigException("table rule " + name + " duplicated!");
            }

            // 提取<tableRule>标签有且唯一的<rule>标签
            // 目前一个<tableRule>只允许有一个<rule>,但从源代码和注释看来,
            // MyCat希望日后能支持多个<rule>,而DBLE则继承了这部分设计
            NodeList ruleNodes = e.getElementsByTagName("rule");
            int length = ruleNodes.getLength();
            if (length > 1) {
                throw new ConfigException("only one rule can defined :" + name);
            }

            // 创建对应的RuleConfig对象,详解在本代码段内
            RuleConfig rule = loadRule((Element) ruleNodes.item(0));

            // 注册RuleConfig对象到XMLRuleLoader的内部集合中
            tableRules.put(name, new TableRuleConfig(name, rule));
        }
    }
}

// ...

private RuleConfig loadRule(Element element) throws SQLSyntaxErrorException {

    // 获取<tableRules>内的<rule>的<columns>标签
    Element columnsEle = ConfigUtil.loadElement(element, "columns");
    String column = columnsEle.getTextContent();

    // <rule>必须有<columns>标签
    if (StringUtil.isEmpty(column)) {
        throw new ConfigException("no rule column is found");
    }

    // <columns>标签目前仅支持1个列作为分片索引
    // 但源代码支持逗号分隔语法,MyCat应该是希望日后能支持
    // 逻辑表的多个列构成一个分片索引(复合分片索引),
    // 而DBLE则继承了这部分设计
    String[] columns = SplitUtil.split(column, ',', true);
    if (columns.length > 1) {
        throw new ConfigException("table rule coulmns has multi values:" +
                columnsEle.getTextContent());
    }

    // <rule>必须要有<algorithm>标签
    Element algorithmEle = ConfigUtil.loadElement(element, "algorithm");
    String algorithmName = algorithmEle.getTextContent();
    if (StringUtil.isEmpty(algorithmName)) {
        throw new ConfigException("algorithm is null or empty");
    }

    // 根据<algorithm>中的内容去找第一次抽象时生成的function
    AbstractPartitionAlgorithm algorithm = functions.get(algorithmName);

    // 如果能找到对应的function,则以此生成新的RuleConfig对象
    if (algorithm == null) {
        throw new ConfigException("can't find function of name :" + algorithmName);
    }
    return new RuleConfig(column.toUpperCase(), algorithmName, algorithm);
}
    原文作者:mysql
    原文地址: https://www.jianshu.com/p/612ebaa5e2de
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞