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">了。

简化名 完全限定类名
hash com.actiontech.dble.route.function.PartitionByLong
stringhash com.actiontech.dble.route.function.PartitionByString
enum com.actiontech.dble.route.function.PartitionByFileMap
numberrange com.actiontech.dble.route.function.AutoPartitionByLong
patternrange com.actiontech.dble.route.function.PartitionByPattern
date com.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);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    Joyyx阅读 8,282评论 0 16
  • 我躺在一个周围尽是白色的地方,乍一看,不知什么原因周围的墙壁上撒着点点的鲜血,闻着一股腥味,我顿觉肚子里翻腾起...
    醉眠1阅读 300评论 0 1
  • 2月的夜晚依旧让人觉得很冷,外面正刮着冷冽的风,而在A城的一所普通宾馆里,房间正哄哄地开着暖气,26度的...
    Joker_Joe阅读 219评论 0 0