精华内容
下载资源
问答
  • 数据挖掘关联规则分析数据集

    热门讨论 2015-10-26 11:55:48
    这是两个关于关联规则挖掘的合成数据集,数据量在10万左右,适用于关联规则分析。
  • 常见开源规则引擎对比分析

    千次阅读 2020-12-30 11:19:52
    电商会员服务的等级、积分、权益等模块中,都使用了大量的规则判断。范式基本上是 达成xxx条件,执行xxx行为 。这很符合规则引擎那一套,因此下面选取了几个业界常见的规则引擎进行分析。 我们分别都从使用角度、...

    0x00 目的背景

    电商会员服务的等级、积分、权益等模块中,都使用了大量的规则判断。范式基本上是 达成xxx条件,执行xxx行为 。这很符合规则引擎那一套,因此下面选取了几个业界常见的规则引擎进行分析。
    我们分别都从使用角度、原理角度两个方面进行分析。

    0x01 easy-rules

    项目地址:https://github.com/j-easy/easy-rules

    1. 规则模型

    规则引擎

    比较容易搞混的概念是Rule,Rule包含了条件和行为。Condition才是用来判断true还是false的条件,Rule还包含了满足条件后需要执行的行为。

    2. 如何使用

    (1) 调用方式

    easy-rules提供了很多种调用方式。使用方式大多都只是个范式,在官网很容易看懂。这里只列举一下各种形式。
    注解方式

    @Rule(name = "my rule1", description = "my rule description", priority = 1)
    public class MyRule1 {
    
        @Condition
        public boolean when(@Fact("type") Integer type) {
            return type == 1;
        }
    
        @Action(order = 1)
        public void execute1(Facts facts) throws Exception {
            log.info("MyRule1 execute1, facts={}", facts);
        }
    
        @Action(order = 2)
        public void execute2(Facts facts) throws Exception {
            log.info("MyRule1 execute2, facts={}", facts);
        }
    }
    

    流式API

    Rule weatherRule = new RuleBuilder()
            .name("weather rule")
            .description("if it rains then take an umbrella")
            .when(facts -> facts.get("rain").equals(true))
            .then(facts -> System.out.println("It rains, take an umbrella!"))
            .build();
    

    表达式方式
    支持 MVEL , SpEL and JEXL

    Rule weatherRule = new MVELRule()
            .name("weather rule")
            .description("if it rains then take an umbrella")
            .when("rain == true")
            .then("System.out.println(\"It rains, take an umbrella!\");");
    

    规则描述文件

    ---
    name: adult rule
    description: when age is greater than 18, then mark as adult
    priority: 1
    condition: "person.age > 18"
    actions:
      - "person.setAdult(true);"
    ---
    name: weather rule
    description: when it rains, then take an umbrella
    priority: 2
    condition: "rain == true"
    actions:
      - "System.out.println(\"It rains, take an umbrella!\");"
    

    (2) 规则组合

    规则的组合,和规则之间的逻辑关系。
    在easy-rules中,规则可以被组合成为 CompositeRule ,并且提供了三种逻辑关系:

    • UnitRuleGroup:规则之间是 AND 的关系,所有规则是一个整体,要么应用所有规则,要么不应用任何规则
    • ActivationRuleGroup:规则之间是 XOR 的关系,只会执行第一个命中的规则,其它规则均忽略
    • ConditionalRuleGroup:具有最高优先级的规则作为 触发条件 ,如果满足触发条件,则会继续执行其它规则

    (3) 规则引擎

    easy-rules提供了两种规则引擎实现:

    • DefaultRulesEngine:根据规则的自然顺序(默认为优先级)应用规则。
    • InferenceRulesEngine:持续对已知事实应用规则,直到不再应用规则为止。

    3. 原理浅析

    我们从Rule看起,了解规则整体模型。然后再看RuleEngine,了解规则加载、判断和执行流程。

    (1) Rule

    类图

    Rule是可比较的,这里主要是方便使用优先级进行排序。Rule是个接口,BasicRule相当于一个Adapter,所有其他Rule都继承BaseRule。

     public interface Rule extends Comparable<Rule> {
        // 省略部分描述和标识性方法
         
    	/**
         * 用于判断Condition是否满足
         */
        boolean evaluate(Facts facts);
    
        /**
         * 用于执行Actions
         * @throws 执行action(s)时如果发生错误会抛出异常
         */
        void execute(Facts facts) throws Exception;
     }
    

    来看一下DefaultRule的实现会更清晰:

    class DefaultRule extends BasicRule {
    
        private final Condition condition;
        private final List<Action> actions;
        // 省略构造方法...
        @Override
        public boolean evaluate(Facts facts) {
            return condition.evaluate(facts);
        }
    
        @Override
        public void execute(Facts facts) throws Exception {
            for (Action action : actions) {
                action.execute(facts);
            }
        }
    }
    

    所以,条件判断、行为执行都是Condition和Action自己做的。我们可以跟进去看看。

    (2) Condition

    Condition就是需要由用户实现的条件接口,实现evaluate即可。easy-rules提供了表达式实现,下面章节会简要分析。

    (3) Action

    Action就是需要由用户实现的条件接口,实现execute即可。easy-rules提供了表达式实现,下面章节会简要分析。

    (4) RulesEngine

    RuleEngine有两种实现,我们以DefaultRulesEngine来分析整体流程。
    抛开那些Listener不谈,RulesEngine有两个主要方法:

    public interface RulesEngine {
        /**
         * 使用facts执行所有已经注册的rules
         */
        void fire(Rules rules, Facts facts);
    
        /**
         * 只检查规则,但是不执行Action,其实就是调用每个Rule的evaluate
         * @return 每个Rule对应的执行结果
         */
        default Map<Rule, Boolean> check(Rules rules, Facts facts) {
            return Collections.emptyMap();
        }
    }
    

    来看看DefaultRulesEngine#doFire

    // 已经省略了部分不重要的代码
    void doFire(Rules rules, Facts facts) {
        if (rules.isEmpty()) {
            return;
        }
        LOGGER.debug("Rules evaluation started");
        for (Rule rule : rules) {
            final String name = rule.getName();
            final int priority = rule.getPriority();
            
            boolean evaluationResult = false;
            try {
                evaluationResult = rule.evaluate(facts);
            } catch (RuntimeException exception) {
                // 异常处理
            }
            if (evaluationResult) {
                LOGGER.debug("Rule '{}' triggered", name);
                try {
                    rule.execute(facts);
                    LOGGER.debug("Rule '{}' performed successfully", name);
                } catch (Exception exception) {
                    // 异常处理
                }
            } else {
                // 异常处理
            }
        }
    }
    

    (5) 表达式

    表达式

    可以看出,easy-rules的条件判断和Action执行,都是基于 表达式引擎 来实现的,是重度依赖表达式引擎的。不过说到底,规则判断可不就是表达式计算么。下面以SpEL为例分析:

    @Override
    public boolean evaluate(Facts facts) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 实际值转换为map作为表达式变量
        context.setRootObject(facts.asMap());
        context.setVariables(facts.asMap());
        if (beanResolver != null) {
            context.setBeanResolver(beanResolver);
        }
        // 执行表达式
        return compiledExpression.getValue(context, Boolean.class);
    }
    

    Action

    @Override
    public void execute(Facts facts) {
        try {
            StandardEvaluationContext context = new StandardEvaluationContext();
            context.setRootObject(facts.asMap());
            context.setVariables(facts.asMap());
            if (beanResolver != null) {
                context.setBeanResolver(beanResolver);
            }
            compiledExpression.getValue(context);
        } catch (Exception e) {
            LOGGER.error("Unable to evaluate expression: '" + expression + "' on facts: " + facts, e);
            throw e;
        }
    }
    

    在提供Action和Condition的时候,实际上就是直接提供对应的表达式语句即可,入参都是String类型的。

    /**
      * Specify the rule's condition as SpEL expression.
      * @param condition of the rule
      * @return this rule
      */
    public SpELRule when(String condition) {
        this.condition = new SpELCondition(condition, parserContext, beanResolver);
        return this;
    }
    
    /**
      * Add an action specified as an SpEL expression to the rule.
      * @param action to add to the rule
      * @return this rule
      */
    public SpELRule then(String action) {
        this.actions.add(new SpELAction(action, parserContext, beanResolver));
        return this;
    }
    

    注意condition表达式和action表达式是分开存储的,所以上面evaluate和execute都是在执行表达式,但是执行的内容却是不同的。

    4. 总结

    easy-rules的代码结构设计的很清晰,代码也很清爽。项目本身很轻量级,基本上只提供了一个规则判断和行为执行的框架,相当于是对计算过程的抽象。
    但也正是因为轻量级,easy-rules几乎不包含规则编排等功能,如果规则的条件本身是很复杂的,那么我们只能自己对这些条件进行编排,对于easy-rules来说,它只是一条单一的规则。当然,你也可以借助EL表达式的力量,实现一些线性编排。

    0x02 Drools

    项目官网:https://www.drools.org/
    源码:https://github.com/kiegroup/drools
    Drools是一个绝对重量级的规则引擎,很多像金融行业、电信行业的大公司都在使用它作为规则引擎。

    1. 规则模型

    • 事实(Fact):对象之间及对象属性之间的关系
    • 规则(rule):是由条件和结论构成的推理语句,一般表示为if…then。一个规则的if部分称为LHS,then部分称为RHS。
    • 模式(module):就是指IF语句的条件。这里IF条件可能是有几个更小的条件组成的大条件。模式就是指的不能在继续分割下去的最小的原子条件。

    2. RETE算法

    Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的算法 , 该算法提供了专家系统的一个高效实现。自 Rete 算法提出以后 , 它就被用到一些大型的规则系统中 , 像 ILog、Jess、JBoss Rules 等都是基于 RETE 算法的规则引擎。
    Forgy的论文原文:RETE Match Algorithm - Forgy OCR.pdf

    Rete 在拉丁语中译为”net”,即网络。Rete 匹配算法是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则。

    其核心思想是将分离的匹配项根据内容动态构造匹配树,以达到显著降低计算量的效果。Rete 算法可以被分为两个部分:规则编译和规则执行。当Rete算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做 match-select-act cycle。

    我们先来了解一些基础概念,下图是一个基本上包含了所有常见元素的RETE网络:
    在这里插入图片描述

    我将RETE网络中的元素整理如下:

    在这里插入图片描述

    RETE算法就是Drools引擎执行速度快的秘诀。个人认为,RETE算法有以下特点:

    • 不同规则之间的相同模式是可以共享节点和存储区的,所以做过的判断不需要重复执行,是典型的空间换时间的做法
    • 使用AlphaMemory和BetaMemory存储事实,当事实部分变化时,可以只计算变化的事实,提高了匹配效率
    • 事实只有满足当前节点,才会向下传递。不做无意义的匹配。

    RETE算法的不足:
    因为RETE是空间换时间,所以当规则和事实很多的时候,可能会耗尽系统资源。

    举例,平台有如下规则:

    • 付费会员生日当天购物满1000元,可获得礼包A
    • 付费会员生日当天购物满500元,可获得礼包B
    • 付费会员生日当天购物满100元,可获得礼包C

    这些规则建立的RETE网络大概是:
    在这里插入图片描述

    RETE网络的创建流程:

    1. 创建根节点
    2. 加入一条规则
      1. 取出规则中的一个模式(模式就是规则中的最小一个匹配项例如: age>10age<20 ),检查模式中的参数类型,如果是新类型(也就是新的Fact类型),则加入一个类型节点;
      2. 检查模式对应的 Alpha 节点是否已存在,如果存在则记录下节点位置,如果没有则将模式作为一个 Alpha 节点加入到网络中,同时根据 Alpha 节点的模式建立 Alpha Memory;
      3. 重复 b 直到所有的模式处理完毕;
      4. 组合 Beta 节点,按照如下方式: Beta 左输入节点为 Alpha(1),右输入节点为 Alpha(2) 。Beta(i) 左输入节点为 Beta(i-1),右输入节点为 Alpha(i) i>2 并将两个父节点的内存表内联成为自己的内存表;
      5. 重复 d 直到所有的 Beta 节点处理完毕;
      6. 将动作(Then 部分)封装成Beta(n) 的输出节点;
    3. 重复2直到所有规则处理完毕

    RETE如何处理模式之间的or关系?由于模式之间只有NOT和EXIST两种关系,Beta Node并不能表述or的关系,所以Drools将其拆分成两条规则来看待。
    在这里插入图片描述

    3. PHREAK算法

    PHREAK是由Drools团队设计和实现的一种算法,并在Drools 6中引入。PHREAK是一种慵懒匹配算法,在模型上基本延续了RETE,节点及其作用都是一样的。
    在这里插入图片描述

    如果你想了解性能差异,这里有一篇PHREAK和RATE算法的性能比较结果。
    http://ksoong.org/drools-examples/content/docs/phreak.html#_phreak_vs_rateoo

    PHREAK算法在RETE之上的改进点:

    • 延迟规则评估 :当PHREAK引擎启动后,所有规则都处于一种 unlinked 的状态,这种状态的规则不会被Drools执行。当 insertupdatedelete 等操作修改了KIE会话的状态时,修改只会传播到alpha子网,并在进入beta子网之前排队。与RETEOO不同,在PHREAK中,不会执行Beta节点以用作这些操作的结果。 引擎会先用试探法确定哪个规则最有可能导致匹配,从而在它们之间强加一个执行顺序。
    • 面向集合传播 :在RETEOO中,每次insert/update/delete事实时,都会从顶部(入口点)到底部(最佳情况下的规则终端节点)遍历网络。网络中执行评估的每个节点都创建了一个元组,该元组传播到路径中的下一个节点。PHREAK不是这样工作的。一个Beta节点上所有排队的insert/update/delete操作会被批处理评估,并且结果会放到一个Set中。这个Set会被转发到下一个节点,并且执行上面同样形式的评估,并把结果放到同一个Set中。面向集合传播在某些规则上具有性能优势,并且为将来做多线程评估提供了可能。
    • 分割网络 :在分段中,一个KIE Base中的节点是可以在不同规则之家共享的。PHREAK将规则是为分段的路径,而不是节点的路径。一个不与其他任何规则共享其节点的规则由单个段组成。路径中每个段都分配有1 bit的标志位。当节点包含足够评估的数据时,该标志位设置为on。当段中所有节点都为on时,段本身将设置为on。当规则的所有段都为on时,该规则会被标记为 linked 。Drools利用这些标志位来避免对已经评估的节点和段进行重新评估,从而让PHREAK网络的评估效率更高。

    想了解更多关于PHREAK的新消息,可参考:http://blog.athico.com/2015/12/drools-detailed-description-of-internal.html

    4. 总结

    Drools发展到今天,其实已经是一整套解决方案了。
    如果只是想要简单使用,那就是只用到Drools的核心BRE,引入几个Maven依赖,编写Java代码和规则文件即可。但是如果要编排很复杂的工程,甚至整个业务都重度依赖,需要产品、运营同学一起来指定规则,则需要用到BRMS整套解决方案了,包括Drools Expert(BRE)、Drools Workbench、DMN。
    所以我们说Drools太重了,主要是在说:

    1. Drools相关的组件太多,需要逐个研究才知道是否需要
    2. Drools逻辑复杂,不了解原理,一旦出现问题排查难度高
    3. Drools需要编写规则文件,学习成本高

    这次分析没有看太多Drools的源码,与easy-rules相比,代码复杂度也不是一个数量级上面的。后面可以再开专题去聊一下Drools的源码,对深入理解RETE或者PHREAK也会有很大帮助。

    0x03 总结

    时间原因,只找了两个最典型的规则引擎项目进行分析,其他像Urule也是用的RETE算法,与Drools的原理大同小异。还有一些轻量级的规则引擎项目,大多也都是简化了规则编排的能力,并依赖表达式引擎做规则判断。

    easy-rulesDrools
    学习成本
    维护成本
    规则编排能力较弱
    执行效率较低
    是否开源开源开源

    0x04 参考资料

    1. [wiki]《Rete algorithm
    2. [博客]《drools -Rete算法》,by 双斜杠少年
    3. [博客]《RETE算法简述 & 实践》,by RyanLee_
    4. [论文]《Rete: A Fast Algorithm for the Many Pattern/Many Object Pattern Match Problem》,by Charles, Forgy
    5. [书籍]《Mastering JBoss Drools 6 for Developers》,by Mauricio Salatino
    6. [代码] github:easy-rules
    7. [代码] github:drools
    展开全文
  • 规则引擎的架构设计与系统实现

    千次阅读 2020-12-17 16:32:41
    业务规则、配置化编程、政策引擎、规则引擎 目录 ruleEngine 1. 背景 2. 方案考察 2.1. 硬编码: 2.2. Drools: 2.3. Urule: 2.4. 自研规则引擎 3. 能力要求 4. 名词解释 5. 概要设计 5.1. 功能模块...

    [未完待续中,后续代码实现优先发布到GitHub]

    https://github.com/failgoddess/ruleEngine

    ruleEngine

    业务规则、配置化编程、政策引擎、规则引擎

     

    目录

    ruleEngine

    1. 背景

    2. 方案考察

    2.1. 硬编码:

    2.2. Drools:

    2.3. Urule:

    2.4. 自研规则引擎

    3. 能力要求

    4. 名词解释

    5. 概要设计

    5.1. 功能模块划分

    5.1.1. 公式推理器Calculator:

    5.1.2. 指标解析器FormulaExecuter:

    5.1.3. 动作执行器ActionExecuter:

    5.1.4. 模型执行器RuleModel:

    5.1.5. 模型构建器RuleModelBuilder:

    5.1.6. 模型加载器RuleModelLoader:

    5.1.7. 交互转换器:

    5.2. 系统设计

    5.2.1. 指标占位符

    5.2.2. JSON出参解析

    5.2.3. 关系运算符

    5.2.4. 技术选型

    5.3. 关键技术说明

    5.3.1. 逻辑式编程

    5.3.2. 分治策略

    5.3.3. 决策树

    5.3.4. 括号匹配算法

    6. 详细设计

    6.1. 数据模型

    6.1.1. 规则ER-图

    6.1.2. 关系数据库表结构

    6.2. 执行逻辑图

    6.2.1. 规则执行时序图

    6.2.2. 指标解析执行流程

    6.3. 缓存对象数据结构

    6.4. 枚举值配置


     

    1. 背景

    在项目步入成熟期,规则类需求几乎占据了业务所有需求的半边天。一方面规则唯一不变的是“多变”,另一方面开发团队对“规则开发”的感受是乏味、疲惫和缺乏技术含量。如何解决规则开发的效率问题,最大化解放开发团队成为一个新的挑战。

    规则底层采用规则引擎来实现。规则引擎是一种嵌入在应用程序中的组件,实现了将业务规则从应用程序代码中分离出来,并使用预定义的规则语义来编写业务规则。规则引擎接受数据输入,解释业务规则,并根据规则执行相应的业务逻辑。一个业务规则包含一组条件和在此条件下执行的操作,它们表示业务规则应用程序的一段业务逻辑。我们在业务中设置一个或者多个条件,当满足这些条件时触发相应的操作,规则引擎设计的初衷是可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件或者数据库中,使得规则的变更不需要修改代码即可使用,做到最大程度的灵活。

    2. 方案考察

    现在市面上在做业务规则的过程中有多种实现方案:

    2.1. 硬编码:

    优点:

    稳定性较佳:语法级别错误不会出现,由编译系统保证。

    当规则较少、变动不频繁时,开发效率最高。

    缺点:

    规则迭代成本高:对规则的少量改动就需要走全流程(开发、测试、部署)。

    规则开发和维护门槛高:规则对业务分析人员不可见。业务分析人员有规则变更需求后无法自助完成开发,需要由开发人员介入开发。

    2.2. Drools:

    优点:

    策略规则和执行逻辑解耦方便维护。

    缺点:

    业务分析师无法独立完成规则配置:由于规则主体DSL是编程语言(支持Java, Groovy, Python),因此仍然需要开发工程师维护。

    规则的语法仅适合扁平的规则,对于嵌套条件语义(then里嵌套when...then子句)的规则只能将条件进行笛卡尔积组合以后进行配置,不利于维护。

    2.3. Urule:

    优点:

    可视化操作完善、功能强大

    缺点:

    不支持回溯:当前分支没有符合条件的之后不支持回溯

    不支持动态加载数据:例如门店有等级、店龄、区域等若干属性,业务规则具体是根据店龄不同给出结果还是根据等级,属于规则的业务范畴,调用方并不关心。要是每次需要将所有可能用于规则判断的数据全部由调用方传入,无疑降低了规则的灵活性。

    2.4. 自研规则引擎

    在学习机器学习中决策树的算法时可以生成决策树的决策流图。决策树整体分为两个阶段:通过算法计算找出规律将规律构建决策模型、传入新数据基于决策流进行新的决策分析。这一点给我带来了很大的思想启发,配置规则的过程作为决策树的构建阶段,新数据决策为决策树的预测过程。出现多个决策路径组合的为最终结果的场景也可以理解决策森林算法的演化。

    3. 能力要求

    规则引擎的设计主要分为两部分:一部分是规则的维护,包括规则的创建、修改、删除;一部分是规则的执行。规则的维护部分侧重点是页面,我们需要将用户在页面上的操作转换为内置规则并保存到数据库中。在规则执行的过程中调用方只需要选择规则传入相应的数据即可获得决策结果。结合整体需求,规则引擎应该有可扩展、易维护的特点,先将规则引擎的功能需要实现的功能点总结如下:

    指标部分(维护指标、计算执行指标)

    模型维护(模型即实体,包括模型的创建以及模型属性的维护)

    规则维护(包括对规则的增删改查)

    条件维护(对规则条件的增删改查)

    指标维护(对规则指标的增删改查)

    结果维护(对规则结果的增删改查)

    节点维护(包括静态节点和动态节点部分)

    基于规则版本的决策记录(基于历史规则查看判定过程记录)

    规则的版本控制

    根据技术考察和能力要求对我们的规则引擎提出了更为全面的要求:

    l 支持可视化的界面配置

    l 支持嵌套条件语义

    l 支持组合条件

    l 支持动态加载

    l 支持决策日志

    l 支持规则历史版本

    l 支持回溯

    l 支持决策森林

    4. 名词解释

    术语及缩略语名词解释
    规则每一个需要用于判定的业务场景就是一套规则,例如:可否邮寄判断;规则通常由每一个决策树的判定结果和操作符构成,例如:区域决策树AND(调整价决策树OR年份决策树);规则的本质是分类问题,由决策树和操作符构建的决策森林
    节点每一个规则内可能影响决策判定流程和结果的一个影响因素(维度)
    结果一个规则中所有决策树中可能返回的结果,也就是一个决策树数的叶子结点,例如:可否邮寄判断的结果集合是是和否
    决策树决策树又称为判定树,是规则中的每一条判定路径,这个判定路径是根据不同节点以及条件执行不同分支路径,完整的判定路径分支、节点就是一棵决策树。考虑到不同连接接有同一个下一跳的情况决策树也被称为决策图
    操作符条件运算符
    分支一个分支包含多个连接是一组连接的集合。
    条件条件由被比较值、操作符、比较值构成。名称参考12/6=2 被除数、除数、商
    连接连接表示当前分支中一个节点在一组条件下要进入的下一跳,下一跳可以是另外一个连接也可以是结果。主要是达到执行对象切换的作用。同一个分支中的不同连接指向同过个下一跳逻辑关系为或的关系,同一个连接中不同的条件为且的关系。
    指标是规则流程执行中的元数据。
    判定日志每一次判定结果的日志记录
    判定数据用于规则判定时的入参对象
    历史规则规则的历史版本控制,每一次规则的修改都会引起一个版本变化,同一时间节点一个规则只会有一个生效的版本

    5. 概要设计

    5.1. 功能模块划分

    5.1.1. 公式推理器Calculator:

    用于计算公式例如((18)/9)9572>1008611计算,是指标解析器的底层实现,公式推理器一部分基于规则引擎实现、一部分基于手写运算符计算器实现

    5.1.2. 指标解析器FormulaExecuter:

    在公式类指标中用于公式的构建例如:目标库存为公式类指标,计算公式为平均周销*周数,由指标解析器将平均周销和周数替换成指定的数值

    在传入类指标中用于将调用方的输入值转化成指标项例如目标库存中周数指标

    在配置类指标中用于将配置到系统中的参数解析成指定指标

    在动作类指标中用于组件构造动作执行器的执行入参并调用动作执行器。

    5.1.3. 动作执行器ActionExecuter:

    根据动作的类型传入参执行方法或者发送相应的请求,并负责解析返回值;不变型动作的执行可基于动作执行历史记录;

    5.1.4. 模型执行器RuleModel:

    负责解析规则模型按照深度优先的规则执行决策树,流程树

    5.1.5. 模型构建器RuleModelBuilder:

    确定一个规则编码负责从数据表中将于这个规则相关的数据加载到程序中,并组装出可以用于执行的规则模型JSON

    5.1.6. 模型加载器RuleModelLoader:

    负责加载配置好的模型,规则当前版本的加载优先级为Cache —> Redis —>模型构建器,规则历史版本的模型直接加载历史版本中的模型JSON

    5.1.7. 交互转换器:

    前端页面本着页面友好的原则会采用多种类型的页面展示和实现,前端数据和后端模型的数据会差异比较大所以在前后交互的过程中采用交互转换器进行数据转换,每种样式的展示方式都会有一个相应的转换器。

    5.2. 系统设计

    5.2.1. 指标占位符

    ${123}:调用id为123的指标

    @{storeCode}:引用入参storeCode的实际值

    %{ now()}:系统内置函数的返回值,这个系统特指规则引擎内部

    #{123}:引用id为123的节点

    ["storeCode","1002"]  向被调用者传入参数storeCode为1002 

    5.2.2. JSON出参解析

    请求型指标会访问一个url发送请求获得非接口返回值是JSON形式的数据,这里采用JsonPath插件对JSON进行解析获取需要的数据。完整语法请参考JsonPath官方文档

    Map或对象:$.attr_name

    List< Map或对象>$. data[i]. attr_name

    i=*或者不配表示 取全部列表每一个item的attr_name逗号隔开

    i=5 代表取索引为5 的attr_name

    5.2.3. 关系运算符

    系统内置操作符用于经销规则的逻辑判断,系统内置的每一条指令都有一个操作符,它表示该指令应进行什么性质的操作。不同的指令用操作符这个字段的不同编码来表示,每一种编码代表一种指令。

    操作符布尔不区分字符串字符串数值时间列表 
    be介于(闭区间)    
    bed介于(开区间)    
    nbe不介于(闭区间)    
    nbed不介于(开区间)    
    cn包含     
    ncn不包含     
    en为空
    nn不为空
    eq等于
    eqd等于(不区分)    
    neq不等于
    neqd不等于(不区分)    
    ge大于等于    
    gt大于    
    in在集合     
    nin不在集合     
    le小于等于    
    lt小于    
    nre不正则    
    re正则    
    and并且   
    or   
    not   
    xor异或   
    lp左括号      
    rp右括号      
    add    
    sub    
    mul     
    div     
    acm取余     

     

    5.2.4. 技术选型

    技术选型目前规则引擎领域开源的主要有Drools、IKExpression、Aviator、QLExpress、SimpleEL、Groovy、Fel等,鉴于Fel表达式求值语法简洁。同时其优异的性能表现,规则中心选型Fel。执行100 万次表达式求值的性能表现对比如下图:

    5.3. 关键技术说明

    本系统会用到:依赖性任务排序的算法(用于指标依赖执行顺序的执行顺序)、决策树算法、分治策略、括号匹配、策略模式、逻辑式编程、JSONPath、DFS算法、Cache、Fel。

    5.3.1. 逻辑式编程

    算法+数据结构=程序这是Pascal设计者Niklaus Wirth的一本著作的书名,它刻画了过程式尤其是结构化编程的思想。后来Robert Kowalski进一步提出:算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的效率。在逻辑式编程中,程序员只需表达逻辑,而控制交给编程语言的解释器或编译器去管理。

    本规则引擎采用逻辑式编程的思想,由业务人员提供业务规则,开发负责将业务规则整理转义成解释器可解释执行的配置文件,在交由解释器和编译器解析执行。则可以做到业务只考虑规则构建、开发需表达逻辑、而执行交给解释器或编译器去管理,当调用方选择规则模型传入初始化数据后有加载器加载指定模型、执行器用相关的决策路径来控制执行返回规则结果达到一部分业务流程可配置的目的。

    5.3.2. 分治策略

    经销规则模块执行器采用分治策略实现深度遍历的决策树。分治策略是对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

    分治法的基本步骤 分治法在每一层递归上都有三个步骤:

    分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

    解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

    合并:将各个子问题的解合并为原问题的解。

    5.3.3. 决策树

    决策树机器学习预测方法,主要用于对离散型数据的分类。一个值经过相应的节点测验会跳过假分支进入真分支,所以一组值经过决策树以后,就会形成从树跟到结果节点的一条唯一路径。所以它除了可以对输入进行分类之外,还能给出如此分类的解释。算法实现分为:特征选择、决策树的训练构建决策模型、模型对新数据测预测;

    规则引擎的数据维度抽取参考特征选择,也是在硬编码中用于if判断的数据字段;决策树模型的构建采用业务人员在实际工作中的经验规则;传入数据由模型执行类比对模型预测的阶段;

    5.3.4. 括号匹配算法

    指标的公式是可以嵌套的所以需要来解析公式需要用到括号匹配算法:

    123 门店平均销量指标 入参门店编码 sku编码 天数

    456 门店可用库存指标 入参门店编码 sku编码

    789 门店需求量指标公式为:门店10天的sku需求量=10天门店最近30天平均该sku销量-门店前库存;可表示为 10${123(storeCode:@{storeCode},skuCode:@{skuCode},dayNumber:30)}-${345(storeCode:@{storeCode},skuCode:@{skuCode})}

    这个解析的过程是一个采用分治策略的递归解析过程;用到括号匹配算法来解析公式;

    原括号匹配算法只是在结构出判断堆栈是否为空用于判断是否成对出现,这里需要做一个改造每一次堆栈为空的时候表示这是一个最大单元也就是整个公式最多可以分为几段 门店需求量就可以分为两段 ${123(storeCode:@{storeCode},skuCode:@{skuCode},dayNumber:30)}和${345(storeCode:@{storeCode},skuCode:@{skuCode})}

    在利用分治递归的思想将每一段分别处理

    6. 详细设计

    6.1. 数据模型

    6.1.1. 规则ER-图

     

    6.1.2. 关系数据库表结构

    节点表

    字段数据类型长度说明
    Idint864节点表
    rule_idint864所属规则ID
    Namevarchar64节点标题
    note_typeint432节点数据来源类型:10传入20指标值
    note_categoryint432节点类型:1可变、0不可变
    Attributevarchar128判定数据对象的字段
    note_categoryint432节点类型:1可变、0不可变
    value_typeint43210数字20时间30字符串35不区分字符串40列表
    parametervarchar255参数用json表示,节点调用指标的参数只能是配置好的固定值或者传入节点的数据值
    quota_idint864指标id
    Remarkvarchar512备注

    结果表

    字段数据类型长度说明
    Idint864结果表
    rule_idint864所属规则ID
    Namevarchar64结果标题
    Datavarchar128结果数据
    result_typeint432结果类型10固定值20节点值30指标值
    node_idint864节点ID
    quota_idint864指标ID
    parametervarchar255参数用json表示
    Remarkvarchar512备注

    规则表

    字段数据类型长度说明
    Idint864规则表
    Namevarchar64规则名称
    Codevarchar64规则编码
    Contentvarchar32规则内容
    data_typevarchar32结果数据的类型
    node_infovarchar32支持的对象节点的描述
    node_attributevarchar32支持的对象节点的属性
    Remarkvarchar255备注

    决策树表

    字段数据类型长度说明
    Idint864经销商商品授权表
    rule_idint864规则ID
    codevarchar64决策树编码
    first_branch_idint864第一跳ID(分支)
    namevarchar64决策树标题
    remarkvarchar255备注

    运算符表

    字段数据类型长度说明
    idint864运算符表
    codevarchar64编码
    operation_typeint432类型:1关系运算符2逻辑运算符3优先级运算符4算数运算符
    namevarchar128运算符标题
    remarkvarchar255备注

    分支表

    字段数据类型长度说明
    idint864分支表
    node_idint864节点ID
    node_typeint864节点数据类型
    namevarchar64条件名称
    operation_idint864操作符ID
    thresholdvarchar32阀值
    rule_idint864规则ID
    tree_idint864决策树ID
    remarkvarchar512备注

    条件表

    字段数据类型长度说明
    idint864条件表
    link_idint864连接ID
    priorityInt464优先级
    namevarchar64条件名称
    operation_idint864操作符ID
    thresholdvarchar32阀值
    rule_idint864规则ID
    branch_idint864分支ID
    threshold_typeInt432阀值类型10固定值20节点值
    note_idint864节点ID
    remarkvarchar512备注

    连接表

    字段数据类型长度说明
    idint864连接表
    namevarchar64连接名称
    priorityint464优先级
    tree_idint864决策树ID
    branch_idint864所属分支ID
    next_branch_idint864下一跳分支ID
    next_typeint432下一跳类型
    next_result_idint864下一跳结果ID
    rule_idint864规则ID
    remarkvarchar512备注

    判定数据表

    字段数据类型长度说明
    idint864判定数据表
    rule_idint864规则ID
    rule_versionvarchar128规则版本号
    datavarchar128判定数据
    resultvarchar64结果(不一定来源于结果)
    result_idint864结果ID(为空则表示规则结果为和合成结果)
    history_idint864历史ID
    factorvarchar128决定因素
    remarkvarchar512备注

    判定日志表

    字段数据类型长度说明
    idint864判定数据表
    rule_idint864规则ID
    rule_versionvarchar128规则版本号
    judge_data_idint864判定数据ID
    flagbool0是否符合
    history_idint864历史ID
    node_idint864节点ID
    link_idint864连接ID
    condition_idint864条件ID
    branch_idint864分支ID
    remarkvarchar512备注

    历史规则表

    字段数据类型长度说明
    idint864历史规则表
    rule_idint864规则ID
    rule_versionvarchar128规则版本号
    start_timetimestamp64开始时间
    end_timetimestamp64结束时间
    remarkvarchar512备注

    指标表

    字段数据类型长度说明
    idint864配置表
    quota_typeint432指标类型10方法类20请求类30公式类40配置类
    namevarchar255指标名称
    value_typeint432数据值类型10数字20时间30字符串35不区分字符串40列表
    java_methodvarchar25510方法类所配置的方法
    request_urlvarchar25520请求类请求路径
    request_methodVarchar25520请求类请求方法,目前只支持get、post
    formulaVarchar25530公式类计算公式
    deployVarchar25540配置类配置
    parameterVarchar255参数用json表示
    feedback_ruleVarchar255返回值取值规则
    necessaryvarchar255必须的入参也就是调用者必传的字段
    cache_imevarchar255请求型缓存时间
    remarkvarchar512备注

    6.2. 执行逻辑图

    6.2.1. 规则执行时序图

     

    6.2.2. 指标解析执行流程

    6.2.2.1. 公式型指标

    10*${123(attr1:12,attr2:${456})}+2

    10*

    ${123(attr1:12,attr2:${456})}

    123 的指标

    解析123指标需要的入参

    attr1=12

    attr2=${456}

    ${456}

    456 的指标

    456 的指标 执行

    123入参构建完成

    123 的指标 执行

    +2

    6.2.2.2. 请求型指标

     

     

    6.3. 缓存对象数据结构

     

    6.4. 枚举值配置

    枚举类型枚举值枚举描述
    节点数据来源类型10传入值
     20指标值
    指标类型10方法类
     20请求类
     30公式类
     40配置类
    结果类型10固定值
     20节点值
     30指标值
    下一跳类型1分支
     2结果
    阀值类型10固定值
     20节点值
    数据值类型10数值
     20时间
     30字符串
     35不区分大小字符串
     40列表

     

    展开全文
  • Fortify SCA 代码规则库-支持Java

    热门讨论 2016-02-27 17:09:29
    Fortify SCA 代码规则库-支持Java,静态代码扫描 Fortify在线规则库网址,符合代码安全的编码参考 Fortify SCA Java
  • 目前,业界主流的规则引擎使用的算法,都是RETE算法。什么是RETE算法呢,以下算法的定义,是从其他官方博客摘抄而来,同时加入了自己的理解。 Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年...

    1.算法简介

    目前,业界主流的规则引擎使用的算法,都是RETE算法。什么是RETE算法呢,以下算法的定义,是从其他官方博客摘抄而来,同时加入了自己的理解。

    Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的算法 , 该算法提供了专家系统的一个高效实现。自 Rete 算法提出以后 , 它就被用到一些大型的规则系统中 , 像 ILog、Jess、JBoss Rules 等都是基于 RETE 算法的规则引擎。

    Rete 在拉丁语中译为”net”,即网络。Rete 匹配算法是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则

    其核心思想是将分离的匹配项根据内容动态构造匹配树(其实是构造一个DAG,有向无环图),以达到显著降低计算量的效果。Rete 算法可以被分为两个部分:规则编译和规则执行 。当 Rete 算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做 match-select-act cycle。本质上是利用空间换换时间。

    2.术语了解

    • Fact,事实对象,对于真实事物或者事实的承载对象,例如:登录事实对象,可能包含:登录ip,用户id,登录设备,近一一小时内登录成功次数,近一小时登录失败次数,可以理解为规则引擎所需要的输入参数。规则引擎会基于Fact对象和规则,构造DAG。

    • Rule,规则,由条件构成和结论构成的推理语句。例如:if ... then ... else .....,if 登录ip in 黑产ip列表内,then 命中登录黑名单 else 放行。这里的规则通指,if..then...else...的原子规则,而非多条件多关系的规则集。
    • LHS,规则的左半部分,通常指规则的if.... 部分。进一步细化,一般是指,具体的左半边因子,操作符,右半边因子。例如:if 登录ip in 黑产ip列表内。左半边因子即登录ip,操作符即in,右半边因子即黑产ip列表。
    • RHS,规则的右半部分,通常指规则的then以及else部分。一般是指具体的action,因为then以及else,往往是对应具体的动作,例如:或者给其他参数赋值(赋值动作),执行其他的函数(执行方法动作)。
    • 知识包,知识包是打包了,某一个业务场景下,所有的规则,所有的库文件(Fact元数据描述库,动作库,常量库,枚举库),甚至包括评分卡,决策流等。一个知识包往往是一个业务场景下大的集合,知识包都有版本概念,可以发布新版本的知识包,当有新的知识包发布,所有依赖某个业务规则的客户端,都会更新为最新知识包下的规则。
    • Session,session代表一次回话,一个回话往往对应一个工作区,即包括整体流程的执行。
    • workmemory,工作区,即执行规则的内存空间,一个workmemory对应一次回话,对应一次规则的执行。
    • Rete,由LHS部分构成的规则网络,通常是dag图。
    • Agenda,议程,决定执行哪些RHS的Action。
    • Action,动作,RHS部分对应的具体动作,例如:赋值,打印参数,执行方法等。

    这里插入一段对DAG的解释,dag即有向无环图,在图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。

    例如:

    3.核心流程

    算法整体分为两部分:1)规则编译和运行时执行,规则编译即把规则编译构造成Rete网络,也即DAG图。2)运行时执行,根据构造的DAG图,带入FACT对象,分配一块workmemory,开始进行匹配执行。

    3.1 规则编译

    rete网络图

    节点解释:

    • root node(输入起始节点)
    • one-input node(单fact的条件匹配)
    • two-input node(多facts的联合join)
    • terminal node(终止节点,走到此节点,则规则匹配完成)

    one-input node再细分为:

    • Type Node:类型节点,有的地方也叫ObjectTypeNode。主要作用:定位事实属性。fact从根节点进入后,会立刻进入TypeNode节点。确定fact的类型,这里是根据class类型去确定,如果是有多个fact对象,则会被拆分为多个TypeNode,例如上文的登录fact则只有一个TypeNode。即上图的红色节点部分
    • Alpha Node:对fact的属性进行匹配判断。Alpha 节点是规则的条件部分的一个模式。通常用于评估字面的条件。例如,登录ip in 黑产ip列表,这个可以理解为一个Alpha Node。在图中AlphaNode未直接体现出来,但是AlphaMemory即AlphaNode所在位置。

    two-input node再细分为:

    • Beta Node:则相对更为复杂一些,需要实现了两个fact的关联与匹配。作用:用来对2个对象进行对比、检查。约定BetaNode的2个输入称为左边(Join Node)和右边。左边通常是一个a list of objects,右边(Not Node)通常是 a single object。每个Bate节点都有自己的终端节点等组成。BetaNode具有记忆功能,左边输入的叫做BetaMemory,会记住所有的语义,右边输入叫做Alpha Memory,会记住所有到达过的对象。
    • Join Node:用于连接操作的节点,相当于and。属于Beta Node类型节点。
    • Not Node:根据右边输入对左边输入的对象数组进行过滤,两个NotNode可以完成 exists 检查。
    • LeftInputAdapterNodes: 将单个对象转化成对象数组。

    例子(来自IBM DEVELOPER论坛):

    rule
    when
        Cheese( $cheddar : name == "cheddar" )
        $person : Person( favouriteCheese == $cheddar )
    then
        System.out.println( $person.getName() + " likes cheddar" );
    end
    rule
    when
        Cheese( $cheddar : name == "cheddar" )
        $person : Person( favouriteCheese != $cheddar )
    then
        System.out.println( $person.getName() + " does not like cheddar" );
    end

    说明:

    两个Fact对象,Cheese与Person,即两个TypeNode, Cheese与Person。即两个Alpha Node,Cheese.name与Person.favouriteCheese,

    但是此处Person.favouriteCheese不能严格称之为AlphaNode,这个应该是BetaNode。

    图 1. RETE 网络

    从图上可以看到,编译后的RETE网络中,AlphaNode是共享的,而BetaNode不是共享的。两条规则的BetaNode的不同。然后这两条规则有各自的Terminal Node。即:AlphaMemory是共享的,BetaMemory是不共享的

    例子(真实的案例):

    3.2 运行时执行

    匹配过程如下:

    1) 对于每个事实,通过 select 操作进行过滤,使事实沿着 rete 网达到合适的 alpha 节点

    2) 对于收到的每一个事实的 alpha 节点,用 Project( 投影操作 ) 将那些适当的变量绑定分离出来。使各个新的变量绑定集沿 rete 网到达适当的 bete 节点

    3) 对于收到新的变量绑定的 beta 节点,使用 Project 操作产生新的绑定集,使这些新的变量绑定沿 rete 网络至下一个 beta 节点以至最后的 Project

    4) 对于每条规则,用 project 操作将结论实例化所需的绑定分离出来。

    如果把 rete 算法类比到关系型数据库操作,则事实集合就是一个关系,每条规则就是一个查询,再将每个事实绑定到每个模式上的操作看作一个 Select 操作,记一条规则为 P,规则中的模式为 c1,c2,…,ci, Select 操作的结果记为 r(ci), 则规则 P 的匹配即为 r(c1)◇r(c2)◇…◇(rci)。其中◇表示关系的连接(Join)操作。

    Rete 网络的连接(Join)和投影 (Project) 和对数据库的操作形象对比,如图所示:

    图 3. join and project

    3.3 Rete算法的特点

    优点:

    a. Rete 算法是一种启发式算法,不同规则之间往往含有相同的模式,因此在 beta-network 中可以共享 BetaMemory 和 betanode。如果某个 betanode 被 N 条规则共享,则算法在此节点上效率会提高 N 倍。

    b. Rete 算法由于采用 AlphaMemory 和 BetaMemory 来存储事实,当事实集合变化不大时,保存在 alpha 和 beta 节点中的状态不需要太多变化,避免了大量的重复计算,提高了匹配效率。

    c. 从 Rete 网络可以看出,Rete 匹配速度与规则数目无关,这是因为事实只有满足本节点才会继续向下沿网络传递。

    不足:

    a. RETE 算法使用了β存储区存储已计算的中间结果, 以牺牲空间换取时间, 从而加快系统的速度。然而β存储区根据规则的条件与事实的数目而成指数级增长, 所以当规则与事实很多时, 会耗尽系统资源 。

    展开全文
  • c++实现关联规则Apriori算法

    热门讨论 2014-10-30 12:18:35
    Apriori算法的c++实现vs2010也适用
  • Java各种规则引擎

    万次阅读 2019-08-21 10:15:14
    Drools规则引擎 简介: Drools就是为了解决业务代码和业务规则分离的引擎。 Drools 规则是在 Java 应用程序上运行的,其要执行的步骤顺序由代码确定 ,为了实现这一点,Drools 规则引擎将业务规则转换成执行树。...

    一. Drools规则引擎

    简介:

    Drools就是为了解决业务代码和业务规则分离的引擎。
    Drools 规则是在 Java 应用程序上运行的,其要执行的步骤顺序由代码确定
    ,为了实现这一点,Drools 规则引擎将业务规则转换成执行树。
    

    特性:

    优点:
       1、简化系统架构,优化应用
       2、提高系统的可维护性和维护成本
       3、方便系统的整合
       4、减少编写“硬代码”业务规则的成本和风险
    

    原理:

    使用方式:

    (1)Maven 依赖:

    <dependencies>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>6.5.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>6.5.0.Final</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    

    (2)新建配置文件/src/resources/META-INF/kmodule.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
        <kbase name="rules" packages="rules">
            <ksession name="myAgeSession"/>
        </kbase>
    </kmodule>
    

    (3)新建drools规则文件/src/resources/rules/age.drl

    import cn.caijiajia.decision.domain.User               // 导入类
    
    dialect  "mvel"
    
    rule "age"                                      // 规则名,唯一
        when
            $user : User(age<15 || age>60)     //规则的条件部分
        then
            System.out.println("年龄不符合要求!");
    end
    

    工程搭建完毕,效果如图:

    测试用例:

    package cn.caijiajia.decision.service;
    
    import cn.caijiajia.decision.domain.User;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.kie.api.KieServices;
    import org.kie.api.runtime.KieContainer;
    import org.kie.api.runtime.KieSession;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/26
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
    public class TestUser {
    
        private static KieContainer container = null;
        private KieSession statefulKieSession = null;
    
        @Test
        public void test(){
            KieServices kieServices = KieServices.Factory.get();
            container = kieServices.getKieClasspathContainer();
            statefulKieSession = container.newKieSession("myAgeSession");
            User user = new User("duval yang",12);
            statefulKieSession.insert(user);
            statefulKieSession.fireAllRules();
            statefulKieSession.dispose();
    
        }
    
    
    
    }
    
    

    二.Aviator表达式求值引擎

    简介:

    Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各
    种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什
    么还需要Avaitor呢?
    
    Aviator的设计目标是轻量级和高性能 ,相比于Groovy、JRuby的笨重,Aviator
    非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法
    是受限的,它不是一门完整的语言,而只是语言的一小部分集合。
    
    其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都
    是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给
    JVM去执行。简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和
    IKExpression这样的轻量级表达式引擎之间。
    

    特性:

    (1)支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、
    正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优
    先级,具体请看后面的操作符列表。
    (2)支持函数调用和自定义函数。
    (3)支持正则表达式匹配,类似Ruby、Perl的匹配语法,并且支持类Ruby的
    $digit指向匹配分组。自动类型转换,当执行操作的时候,会自动判断操作数类
    型并做相应转换,无法转换即抛异常。
    (4)支持传入变量,支持类似a.b.c的嵌套变量访问。
    (5)性能优秀。
    (6)Aviator的限制,没有if else、do while等语句,没有赋值语句,仅支持逻
    辑表达式、算术表达式、三元表达式和正则匹配。没有位运算符
    
    

    整体结构:

    maven依赖:

    <dependency>
        <groupId>com.googlecode.aviator</groupId>
        <artifactId>aviator</artifactId>
        <version>${aviator.version}</version>
    </dependency>
    

    执行方式

    执行表达式的方法有两个:execute()、exec();
    execute(),需要传递Map格式参数
    exec(),不需要传递Map

    示例:

    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            // exec执行方式,无需传递Map格式
            String age = "18";
            System.out.println(AviatorEvaluator.exec("'His age is '+ age +'!'", age));
    
    
    
            // execute执行方式,需传递Map格式
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("age", "18");
            System.out.println(AviatorEvaluator.execute("'His age is '+ age +'!'", 
    map));
    
        }
    }
    

    使用函数

    Aviator可以使用两种函数:内置函数、自定义函数
    (1)内置函数

    package cn.caijiajia.decision.service;
    
    import com.googlecode.aviator.AviatorEvaluator;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            Map<String,Object> map = new HashMap<>();
            map.put("s1","123qwer");
            map.put("s2","123");
    
      System.out.println(AviatorEvaluator.execute("string.startsWith(s1,s2)",map));
    
        }
    }
    
    
    

    (2)自定义函数

    自定义函数要继承AbstractFunction类,重写目标方法。

    
    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    import com.googlecode.aviator.runtime.function.AbstractFunction;
    import com.googlecode.aviator.runtime.function.FunctionUtils;
    import com.googlecode.aviator.runtime.type.AviatorDouble;
    import com.googlecode.aviator.runtime.type.AviatorLong;
    import com.googlecode.aviator.runtime.type.AviatorObject;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            // 注册自定义函数
            AviatorEvaluator.addFunction(new MultiplyFunction());
            // 方式1
            System.out.println(AviatorEvaluator.execute("multiply(12.23, -2.3)"));
            // 方式2
            Map<String, Object> params = new HashMap<>();
            params.put("a", 12.23);
            params.put("b", -2.3);
            System.out.println(AviatorEvaluator.execute("multiply(a, b)", params));
        }
    
    }
    
    class MultiplyFunction extends AbstractFunction{
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
    
            double num1 = FunctionUtils.getNumberValue(arg1, env).doubleValue();
            double num2 = FunctionUtils.getNumberValue(arg2, env).doubleValue();
            return new AviatorDouble(num1 * num2);
        }
    
        @Override
        public String getName() {
            return "multiply";
        }
    
    }
    

    常用操作符的使用

    (1)操作符列表

    (2)常量和变量

    (3)编译表达式

    package cn.caijiajia.decision.service;
    
    import com.googlecode.aviator.AviatorEvaluator;
    import com.googlecode.aviator.Expression;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
    * CreateBy: Liurenquan
    * CreateDate: 2018/12/25
    */
    public class Test {
       public static void main(String[] args) {
           String expression = "a+(b-c)>100";
           // 编译表达式
           Expression compiledExp = AviatorEvaluator.compile(expression);
    
           Map<String, Object> env = new HashMap<>();
           env.put("a", 100.3);
           env.put("b", 45);
           env.put("c", -199.100);
    
           // 执行表达式
           Boolean result = (Boolean) compiledExp.execute(env);
           System.out.println(result);
    
       }
    }
    
    

    (4) 访问数组和集合
    List和数组用list[0]和array[0],Map用map.date

    package cn.caijiajia.decision.service;
    
    import com.googlecode.aviator.AviatorEvaluator;
    import com.googlecode.aviator.Expression;
    
    import java.util.*;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
    
            final List<String> list = new ArrayList<>();
            list.add("hello");
            list.add(" world");
    
            final int[] array = new int[3];
            array[0] = 0;
            array[1] = 1;
            array[2] = 3;
    
            final Map<String, Date> map = new HashMap<>();
            map.put("date", new Date());
    
            Map<String, Object> env = new HashMap<>();
            env.put("list", list);
            env.put("array", array);
            env.put("map", map);
    
            System.out.println(AviatorEvaluator.execute(
                    "list[0]+':'+array[0]+':'+'today is '+map.date", env));
    
        }
    
    }
    

    (5) 三元比较符

    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
    
            Map<String, Object> env = new HashMap<String, Object>();
            env.put("a", -5);
            String result = (String) AviatorEvaluator.execute("a>0? 'yes':'no'", env);
            System.out.println(result);
        }
    
    }
    

    (6) 正则表达式匹配

    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            String email = "hello2018@gmail.com";
            Map<String, Object> env = new HashMap<String, Object>();
            env.put("email", email);
            String username = (String) AviatorEvaluator.execute("email=~/([\\w0-8]+)@\\w+[\\.\\w+]+/ ? $1 : 'unknow' ", env);
            System.out.println(username);
        }
    }
    

    (7) 变量的语法糖衣

    
    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            User user = new User(1,"jack","18");
            Map<String, Object> env = new HashMap<>();
            env.put("user", user);
    
            String result = (String) AviatorEvaluator.execute(" '[user id='+ user.id + ',name='+user.name + ',age=' +user.age +']' ", env);
            System.out.println(result);
        }
    }
    
    
    
    package cn.caijiajia.decision.controller;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class User {
    
        private int id;
    
        private String name;
    
        private String age;
    
        public User() {
        }
    
        public User(int id, String name, String age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    
    }
    

    (8) nil对象[任何对象都比nil大除了nil本身]

    nil是Aviator内置的常量,类似java中的null,表示空的值。nil跟null不同的在
    于,在java中null只能使用在==、!=的比较运算符,而nil还可以使用>、>=、
    <、<=等比较运算符。Aviator规定,[任何对象都比nil大除了nil本身]。用户传入
    的变量如果为null,将自动以nil替代。
    
            AviatorEvaluator.execute("nil == nil");  //true 
            AviatorEvaluator.execute(" 3> nil");    //true 
            AviatorEvaluator.execute(" true!= nil");    //true 
            AviatorEvaluator.execute(" ' '>nil ");  //true 
            AviatorEvaluator.execute(" a==nil ");   //true,a is null
    nil与String相加的时候,跟java一样显示为null
    
    

    (9) 日期比较

    package cn.caijiajia.decision.controller;
    
    import com.googlecode.aviator.AviatorEvaluator;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/25
     */
    public class Test {
        public static void main(String[] args) {
            Map<String, Object> env = new HashMap<String, Object>();
            final Date date = new Date();
            String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(date);
            env.put("date", date);
            env.put("dateStr", dateStr);
    
            Boolean result = (Boolean) AviatorEvaluator.execute("date==dateStr",
     env);
            System.out.println(result);
    
            result = (Boolean) AviatorEvaluator.execute("date > '2009-12-20 
    00:00:00:00' ", env);
            System.out.println(result);
    
            result = (Boolean) AviatorEvaluator.execute("date < '2200-12-20 
    00:00:00:00' ", env);
            System.out.println(result);
    
            result = (Boolean) AviatorEvaluator.execute("date ==date ", env);
            System.out.println(result);
    
    
        }
    }
    

    (10) 语法手册

    数据类型

    • Number类型:数字类型,支持两种类型,分别对应Java的Long和Double,也就是说任何整数都将被转换为Long,而任何浮点数都将被转换为Double,包括用户传入的数值也是如此转换。不支持科学计数法,仅支持十进制。如-1、100、2.3等。

    • String类型: 字符串类型,单引号或者双引号括起来的文本串,如'hello world',变量如果传入的是String或者Character也将转为String类型。

    • Bool类型: 常量true和false,表示真值和假值,与java的Boolean.TRUE和Boolean.False对应。

    • Pattern类型: 类似Ruby、perl的正则表达式,以//括起来的字符串,如//d+/,内部实现为java.util.Pattern。

    • 变量类型: 与Java的变量命名规则相同,变量的值由用户传入,如"a"、"b"等

    • nil类型: 常量nil,类似java中的null,但是nil比较特殊,nil不仅可以参与==、!=的比较,也可以参与>、>=、<、<=的比较,Aviator规定任何类型都n大于nil除了nil本身,nil==nil返回true。用户传入的变量值如果为null,那么也将作为nil处理,nil打印为null。

    算术运算符

    Aviator支持常见的算术运算符,包括+ - <tt></tt> / % 五个二元运算符,和一元运算符"-"。其中 - <tt></tt> / %和一元的"-"仅能作用于Number类型。

    "+"不仅能用于Number类型,还可以用于String的相加,或者字符串与其他对象的相加。Aviator规定,任何类型与String相加,结果为String。

    逻辑运算符

    Avaitor的支持的逻辑运算符包括,一元否定运算符"!",以及逻辑与的"&&",逻辑或的"||"。逻辑运算符的操作数只能为Boolean。

    关系运算符

    Aviator支持的关系运算符包括"<" "<=" ">" ">=" 以及"=="和"!=" 。
    &&和||都执行短路规则。

    关系运算符可以作用于Number之间、String之间、Pattern之间、Boolean之间、变量之间以及其他类型与nil之间的关系比较,不同类型除了nil之外不能相互比较。

    Aviator规定任何对象都比nil大除了nil之外。

    匹配运算符

    匹配运算符"=~"用于String和Pattern的匹配,它的左操作数必须为String,右操作数必须为Pattern。匹配成功后,Pattern的分组将存于变量$num,num为分组索引。

    三元运算符

    Aviator没有提供if else语句,但是提供了三元运算符 "?:",形式为 bool ? exp1: exp2。 其中bool必须为结果为Boolean类型的表达式,而exp1和exp2可以为任何合法的Aviator表达式,并且不要求exp1和exp2返回的结果类型一致。

    两种模式
    默认AviatorEvaluator以编译速度优先:
    AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
    你可以修改为运行速度优先,这会做更多的编译优化:
    AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);

    三.MVEL表达式解析器

    简介:

    MVEL在很大程度上受到Java语法的启发,作为一个表达式语言,也有一些根本
    的区别,旨在更高的效率,例如:直接支持集合、数组和字符串匹配等操作以
    及正则表达式。 MVEL用于执行使用Java语法编写的表达式。
    

    特性:

    MVEL是一个功能强大的基于Java应用程序的表达式语言。
    目前最新的版本是2.0,具有以下特性:
    (1). 动态JIT优化器。当负载超过一个确保代码产生的阈值时,选择性地产生字
    节代码,这大大减少了内存的使用量。新的静态类型检查和属性支持,允许集成
    类型安全表达。
    (2). 错误报告的改善。包括行和列的错误信息。
    (3). 新的脚本语言特征。MVEL2.0 包含函数定义,如:闭包,lambda定义,
    标准循环构造(for, while, do-while, do-until…),空值安全导航操作,内联with
    -context运营 ,易变的(isdef)的测试运营等等。
    (4). 改进的集成功能。迎合主流的需求,MVEL2.0支持基础类型的个性化属性处理器,集成到JIT中。
    (5). 更快的模板引擎,支持线性模板定义,宏定义和个性化标记定义。
    (6). 新的交互式shell(MVELSH)。
    
    (7). 缺少可选类型安全
    (8). 集成不良,通常通过映射填入内容。没有字节码不能运作用字节码生成编
    译时间慢,还增加了可扩展性问题;不用字节码生成运行时执行非常慢
    (9). 内存消耗过大
    (10). Jar巨大/依赖规模
    

    原理:

    与java不同,MVEL是动态类型(带有可选分类),也就是说在源文件中是没有
    类型限制的。一条MVEL表达式,简单的可以是单个标识符,复杂的则可能是
    一个充满了方法调用和内部集合创建的庞大的布尔表达式。
    

    使用方式:

    maven引入jar:

    <dependency>
        <groupId>org.mvel</groupId>
        <artifactId>mvel2</artifactId>
        <version>2.3.1.Final</version>
    </dependency>
    
    

    测试:

    package com.lrq.wechatdemo.utils;
    
    import com.google.common.collect.Maps;
    import org.mvel2.MVEL;
    
    import java.util.Map;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/26
     */
    public class MvelUtils {
    
        public static void main(String[] args) {
            String expression = "a == null && b == nil ";
            Map<String,Object> map = Maps.newHashMap();
            map.put("a",null);
            map.put("b",null);
    
            Object object = MVEL.eval(expression,map);
            System.out.println(object);
        }
    
    }
    

    四.EasyRules规则引擎

    简介:

    easy-rules首先集成了mvel表达式,后续可能集成SpEL的一款轻量
    级规则引擎
    

    特性:

    easy rules是一个简单而强大的java规则引擎,它有以下特性:
    
    轻量级框架,学习成本低
    基于POJO
    为定义业务引擎提供有用的抽象和简便的应用
    从原始的规则组合成复杂的规则
    它主要包括几个主要的类或接口:Rule,RulesEngine,RuleListener,Facts 
    还有几个主要的注解:@Action,@Condition,@Fact,@Priority,@Rule
    

    使用方式:

    @Rule可以标注name和description属性,每个rule的name要唯一,
    如果没有指定,则RuleProxy则默认取类名
    @Condition是条件判断,要求返回boolean值,表示是否满足条件
    
    @Action标注条件成立之后触发的方法
    
    @Priority标注该rule的优先级,默认是Integer.MAX_VALUE - 1,值
    越小越优先
    
    @Fact 我们要注意Facts的使用。Facts的用法很像Map,它是客户
    端和规则文件之间通信的桥梁。在客户端使用put方法向Facts中添
    加数据,在规则文件中通过key来得到相应的数据。
    

    有两种使用方式:

    (1)java方式
    首先先创建规则并标注属性

    package com.lrq.wechatdemo.rules;
    
    import org.jeasy.rules.annotation.Action;
    import org.jeasy.rules.annotation.Condition;
    import org.jeasy.rules.annotation.Fact;
    import org.jeasy.rules.annotation.Rule;
    import org.jeasy.rules.support.UnitRuleGroup;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/26
     */
    public class RuleClass {
    
        @Rule(priority = 1) //规则设定优先级
        public static class FizzRule {
            @Condition
            public boolean isFizz(@Fact("number") Integer number) {
                return number % 5 == 0;
            }
    
            @Action
            public void printFizz() {
                System.out.print("fizz\n");
            }
        }
    
        @Rule(priority = 2)
        public static class BuzzRule {
            @Condition
            public boolean isBuzz(@Fact("number") Integer number) {
                return number % 7 == 0;
            }
    
            @Action
            public void printBuzz() {
                System.out.print("buzz\n");
            }
        }
    
        public static class FizzBuzzRule extends UnitRuleGroup {
    
            public FizzBuzzRule(Object... rules) {
                for (Object rule : rules) {
                    addRule(rule);
                }
            }
    
            @Override
            public int getPriority() {
                return 0;
            }
        }
    
        @Rule(priority = 3)
        public static class NonFizzBuzzRule {
    
            @Condition
            public boolean isNotFizzNorBuzz(@Fact("number") Integer number) {
                // can return true, because this is the latest rule to trigger according to
                // assigned priorities
                // and in which case, the number is not fizz nor buzz
                return number % 5 != 0 || number % 7 != 0;
            }
    
            @Action
            public void printInput(@Fact("number") Integer number) {
                System.out.print(number+"\n");
            }
        }
    
    }
    

    然后客户端调用

    package com.lrq.wechatdemo.rules;
    
    import org.jeasy.rules.api.Facts;
    import org.jeasy.rules.api.Rules;
    import org.jeasy.rules.api.RulesEngine;
    import org.jeasy.rules.core.DefaultRulesEngine;
    import org.jeasy.rules.core.RulesEngineParameters;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/26
     */
    public class RuleJavaClient {
        public static void main(String[] args) {
            // 创建规则引擎
            RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
            RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
    
            // 创建规则集并注册规则
            Rules rules = new Rules();
            rules.register(new RuleClass.FizzRule());
            rules.register(new RuleClass.BuzzRule());
            rules.register(new RuleClass.FizzBuzzRule(new RuleClass.FizzRule(), new RuleClass.BuzzRule()));
            rules.register(new RuleClass.NonFizzBuzzRule());
    
            // 执行规则
            Facts facts = new Facts();
            for (int i = 1; i <= 100; i++) {
                facts.put("number", i);
                fizzBuzzEngine.fire(rules, facts);
                System.out.println();
            }
        }
    
    }
    
    

    (2)yml方式

    resources目录下新建fizzbuzz.yml

    ---
    name: "fizz rule"
    description: "print fizz if the number is multiple of 5"
    priority: 1
    condition: "number % 5 == 0"
    actions:
    - "System.out.println(\"fizz\")"
    
    ---
    name: "buzz rule"
    description: "print buzz if the number is multiple of 7"
    priority: 2
    condition: "number % 7 == 0"
    actions:
    - "System.out.println(\"buzz\")"
    
    ---
    name: "fizzbuzz rule"
    description: "print fizzbuzz if the number is multiple of 5 and 7"
    priority: 0
    condition: "number % 5 == 0 && number % 7 == 0"
    actions:
    - "System.out.println(\"fizzbuzz\")"
    
    ---
    name: "non fizzbuzz rule"
    description: "print the number itself otherwise"
    priority: 3
    condition: "number % 5 != 0 || number % 7 != 0"
    actions:
    - "System.out.println(number)"
    

    客户端调用:

    package com.lrq.wechatdemo.rules;
    
    import org.jeasy.rules.api.Facts;
    import org.jeasy.rules.api.Rules;
    import org.jeasy.rules.api.RulesEngine;
    import org.jeasy.rules.core.DefaultRulesEngine;
    import org.jeasy.rules.core.RulesEngineParameters;
    import org.jeasy.rules.mvel.MVELRuleFactory;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    
    /**
     * CreateBy: Liurenquan
     * CreateDate: 2018/12/26
     */
    public class RuleYmlClient {
    
        public static void main(String[] args) throws FileNotFoundException {
            // create a rules engine
            RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
            RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
    
            // create rules
            Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("fizzbuzz.yml"));
    
            // fire rules
            Facts facts = new Facts();
            for (int i = 1; i <= 100; i++) {
                facts.put("number", i);
                fizzBuzzEngine.fire(rules, facts);
                System.out.println();
            }
        }
    }

    作者:HaleyLiu
    链接:https://www.jianshu.com/p/41ea7a43093c
    来源:简书

    展开全文
  • Sentinel规则之流量控制规则

    千次阅读 2019-10-25 16:45:34
    Sentinel规则之流量控制规则 文章目录Sentinel规则之流量控制规则概述继承结构基本代码基于QPS流量控制流量控制代码测试基于并发线程数控制基于调用关系的流量控制根据调用方限流根据调用链路限流具有关系的资源流量...
  • Makefile系列之规则

    千次阅读 2018-08-13 22:17:46
    规则 语法 TARGETS:PREREQUISITES (; COMMAND) COMMAND … TARGETS : 目标,可以是空格分开的多个文件,也可使用通配符; PREREQUITITES : 依赖,根据依赖更新后是否需要重建目标,将依赖分为两种,用“|...
  • suricata规则

    千次阅读 2018-12-20 10:21:07
    Emerging威胁检测规则wiki:http://doc.emergingthreats.net/bin/view/Main/WebHome ...Snort规则与Suricata规则基本语法相同 规则结构 一、规则头部: 1. 规则行为:行为声明,用于通知IDS引擎...
  • ModSecurity规则

    千次阅读 2019-05-04 08:26:42
    现在的ModSecurity已经成功配置了OWASP的规则,但能够按照我们的选择让它工作,我们将不得不作出一个新的配置文件与我们自己的规则,这就是所谓的白名单文件。通过这个文件,我们就可以控制整个防火墙,创建自己的...
  • Thingsboard 规则引擎规则链入门教程

    千次阅读 2020-05-01 10:59:27
    一、规则引擎是什么? 官方解释:“规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并...
  • 规则引擎概述及选型

    万次阅读 2019-03-01 16:57:01
    规则引擎概述及选型
  • AD规则详细介绍

    万次阅读 多人点赞 2019-11-03 11:36:26
    作为一个画板初学者,大家可能只是在画板当中哪出错了才会去修改规则,一个专业画板师是不会这样被动的喲,接下来我来介绍AD16规则。 **一:**Electrical Clearance: a:线与线之间距离、焊盘与焊盘之间距离、...
  • iptables详解及docker的iptables规则

    千次阅读 2020-10-31 20:14:32
    iptables详解及docker的iptables规则iptables处理流程iptables命令ipset的使用ftp服务规则SNAT与DNATfirewall-cmd命令docker的iptables规则docker网络类型数据包处理流程 iptables 处理流程 iptables命令 ipset的...
  • 分类:基于规则的分类技术

    千次阅读 2018-09-30 16:13:05
    基于规则的分类是一种比较简单的分类技术,下面从以下几个方面对其进行介绍  1.任务  所有的分类技术的任务都是利用数据集训练出分类器,然后为每条记录贴上标签,对其进行分类,基于规则的分类任务也是如此。 ...
  • Flink-Cep实现规则动态更新

    千次阅读 2020-11-30 21:47:42
    Flink-Cep实现规则动态更新 规则引擎通常对我们的理解就是用来做模式匹配的,在数据流里面检测满足规则要求的数据。有人会问为什么需要规则动态变更呢?直接修改了规则把服务重启一下不就可以了吗,这个当然是不行...
  • as-if-serial规则和happens-before规则的区别

    千次阅读 多人点赞 2019-10-04 15:17:39
    为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度,我们需要了解as-if-serial规则和happens-before规则 as-if-serial规则 as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度...
  • 规则引擎在基础软件,或者在很多系统中已经不是稀奇的玩意,最近这几年,国内不断兴起很多的规则引擎,至于什么是规则引擎,在这篇文章中,就不做介绍了,我想能看以下内容的,多少对规则引擎也都有所了解了。...
  • 开源规则引擎 drools

    万次阅读 多人点赞 2018-08-06 21:30:34
    在很多企业的 IT 业务系统中,经常会有大量的业务规则配置,而且随着企业管理者的决策变化,这些业务规则也会随之发生更改。为了适应这样的需求,我们的 IT 业务系统应该能快速且低成本的更新。适应这样的需求,一般...
  • C语言标识符命名规则

    万次阅读 多人点赞 2018-05-27 14:46:40
    C语言的标识符命名规则 C语言的标识符一般应遵循如下的命名规则: 1 标识符必须以字母a~z、 A~Z或下划线开头,后面可跟任意个(可为0)字符,这些字符可以是字母、下划线和数字,其他字符不允许出现在标识符中。...
  • 关联规则与Apriori算法

    万次阅读 多人点赞 2019-08-15 22:00:07
    也会有一个比较低的置信度,和之前一样,可以使用Apriori算法修剪较低级别的候选项规则,从而减少需要检查的候选规则。 局限性 计算代价很高。 尽管Apriori算法减少了要考虑的候选项集的数量,但是当存储库存...
  • 机器学习(十二)-基于规则的分类器

    万次阅读 多人点赞 2019-06-11 09:22:47
    本文主要介绍给予规则的分类器的相关内容
  • Java规则引擎工作原理及其应用

    千次阅读 2018-12-28 22:25:48
    Java规则引擎是一种嵌入在Java程序中的组件,它的任务是把当前提交给引擎的Java数据对象与加载在引擎中的业务规则进行测试和比对,激活那些符合当前数据状态下的业务规则,根据业务规则中声明的执行逻辑,触发应用...
  • WPF不规则形状按钮

    热门讨论 2012-05-22 20:26:31
    WPF复杂形状按钮,PNG,图片类按钮。
  • 数字逻辑中的反演规则和对偶规则

    千次阅读 2021-07-05 18:04:05
    数字逻辑中反演规则与对偶规则
  • 规则提取介绍

    千次阅读 2018-04-24 18:45:38
     模型的规则用析取范式 R =(r1 ∨ r2 ∨ ••• ∨ rk)表示,其中R称作规则集,ri 是分类规则或析取项。  每一个分类规则可以表示为如下形式: ri:(条件i)→yi 规则左边成为规则前件或前提。它是属性测试的合...
  • Altium Designer 布线规则设定

    千次阅读 2018-07-27 20:32:03
    对于 PCB 的设计, Altium Designer .0提供了详尽的 10 种不同的设计规则,这些设计规则则包括导线放置、导线布线方法、元件放置、布线规则、元件移动和信号完整性等规则。根据这些规则, Protel DXP 进行自动布局和...
  • OpenCV截取图像的任意区域(ROI),规则的图形(圆、椭圆、矩形),不规则鼠标自己选择.
  • 双跑:规则在场景中的状态,双跑状态的规则逻辑将灰度执行,但仅会出现在修改生效规则、因子(因子修改导致引用规则的逻辑执行变化)时,才会存在的状态。 回溯:规则在场景中的状态,回溯状态的规则逻辑将灰度执行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,795,376
精华内容 1,118,150
关键字:

规则