1. 首页 > 基金定投

规则引擎有哪些(EasyRules 规则引擎)

前言

接手了一个运维项目,查看其中的代码,中间有接近 100 个 if else 判断,全部采用硬编码的方式。代码特别长,规则十分混乱,不好管理。

  • 如何处理这些问题?
  • 采用什么方式,将业务规则抽象出来?

我建议采用 Easy Rules 规则引擎。不光可以使代码变得优雅,同时还可以降低项目的运维难度,提升项目的健壮性。

我将从如下 4 点进行分析:

  • Easy Rules 是什么?它的原理是什么?涉及概念解释。
  • Easy Rules 的原理是什么?
  • Easy Rules 怎么使用?如何创建 rule、fact 等?
  • 手把手写 Demo 使用 Easy Rules 规则引擎。

Easy Rules 是什么?涉及概念解释

Easy rules 是什么?

Easy Rules 是一款开源的 Java 规则引擎,她将业务规则抽象出来统一管理,让复杂的多变的业务逻辑变得业务清晰。她支持简单规则创建组合规则、支持表达式语言、简单易用的 API、基于 POJO 的注解式编程模型。

规则引擎的目的就是要以松散灵活的方式来替代硬编码式的方式,使代码逻辑变得清晰、松耦合。告别传统 if else 判断的模式。

Easy Rules 的组成部分

事实(Fact)

业务数据,例如:我们 Java 里面的 bean 对象实例。

Product product = new Product();

product 这个对象就是一个事实对象。

规则(Rule)

业务规则,包含业务条件和满足条件后的结果。

例如:满 1000 元送 200 元优惠券,满 500 元,送 100 元优惠券,少于 200 则不送任何优惠。这 就是一条业务规则。

规则引擎(Rule Engine)

它的作用就是执行规则,将事实 fact 和规则 rule 都加载到规则引擎后,规则引擎将两者进行匹配,得到最后的行动结果 action。

规则监听(Rule Listener)

它的目的是监听规则的执行的效果。

注解名词解释

  • name:名称,规则名称。名称必须 唯一不能重复 。
  • Description:规则的描述,有利于开发者理解规则的含义。
  • Priority:优先级,给规则设定优先级别,0 的优先级高于 1。
  • Conditions:条件,业务规则里面的一种条件。通常会有多个业务规则组合。
  • Actions:动作,满足 某个业务规则后,会执行的动作。

Easy Rules 的原理是什么?

原理:

  • 创建事实库,将业务对象添加到事实库里面。
  • 创建规则库,加载业务 rule 到内存当中。
  • 规则引擎,将事实库和规则库进行匹配,获取符合要求的规则。
  • 执行器执获取到匹配的规则后,执行规则中的 action。

规则引擎通过匹配规则库和事实库,挑选出对应的规则,最后执行对应的 action。如下图:

 

Easy Rules 怎么使用?如何创建 rule、fact 等?

创建一个默认规则引擎

1. 先创建引擎参数 RulesEngineParameters

  • priorityThreshold 属性:大于指定优先级的规则不执行
  • skipOnFirstAppliedRule 属性:默认为 false,如果为 true 从一条开始,只执行匹配成功的第一条。
  • skipOnFirstFailedRule 属性:默认为 false,如果为 true,规则中抛出异常,就会停止执行后续规则。
  • skipOnFirstNonTriggeredRule 属性:默认为 false,如果为 true,从第一条开始匹配,匹配成功才会继续执行后续规则。

2. 再创建默认引擎,并传递入参

    RulesEngineParameters parameters = new RulesEngineParameters()
            .priorityThreshold(1000000) 
            .skipOnFirstAppliedRule(true)
            .skipOnFirstFailedRule(true)
            .skipOnFirstNonTriggeredRule(true);
    rulesEngine = new DefaultRulesEngine(parameters);

创建 facts

facts 实际就是我们的业务对象。

我们创建了一个商品对象,并且定义了商品名称和价格。

    /**
     * 创建一个商品对象作为 fact,其中包含商品名称和价格
     */
    public class Product {

        private String productName;

        private BigDecimal prices;

        public String getProductName() {
            return productName;
        }

        public BigDecimal getPrices() {
            return prices;
        }

        public void setProductName(String productName) {
            this.productName = productName;
        }

        public void setPrices(BigDecimal prices) {
            this.prices = prices;
        }
    }

定义规则

定义规则可以有多种方式,如下挨个介绍。我们可以根据项目情况,选择适合自己的方式。

第一个方式:注解方式定义一个规则

    import org.jeasy.rules.annotation.Action;
    import org.jeasy.rules.annotation.Condition;
    import org.jeasy.rules.annotation.Fact;
    import org.jeasy.rules.annotation.Rule;

    @Rule(name = "rules01", description = "1 号规则-满 100 元,赠送 20 元购物券")
    public class Rules01 {

        /**
         * 判断条件是否满足
         */
        @Condition
        public boolean productPrice(@Fact("product")Product product) {
            return product.getPrices().doubleValue() > 100;
        }

        /**
         * 执行行动
         */
        @Action
        public void send() {
            System.out.println(">>>>>>>>>>>>>满 100 元,赠送 20 元购物券");
        }
    }

第二个方式:链式编程的方式定义规则

    public static void main(String[] args) {
        //创建规则 02
        Rule Rule02 = new RuleBuilder()
                .name("rule02")
                .description("规则 02-满 300 元,送 40 的优惠券")
                .when(facts -> {
                    Product product = (Product)facts.get("product");
                    return product.getPrices().doubleValue()>=300;
                })
                .then(facts -> {
                    Product product = (Product)facts.get("product");
                    System.out.println("商品名称:"+product.getProductName()+",优惠价格是:"+product.getPrices().add(new BigDecimal(-40)));
                }).build();

        //创建规则 03
        Rule Rule03 = new RuleBuilder()
                .name("rule03")
                .description("规则 03-满 500 元,送 80 的优惠券")
                .when(facts -> {
                    Product product = (Product)facts.get("product");
                    return product.getPrices().doubleValue()>=500;
                })
                .then(facts -> {
                    Product product = (Product)facts.get("product");
                    System.out.println("商品名称:"+product.getProductName()+",优惠价格是:"+product.getPrices().add(new BigDecimal(-80)));
                }).build();

        //添加事实对象
        Facts facts = new Facts();
        Product product = new Product();
        product.setPrices(new BigDecimal(500));
        product.setProductName("衣服");
        facts.put("product", product);

        //注册规则
        Rules rules = new Rules();
        rules.register(Rule02);
        rules.register(Rule03);
        //规则引擎执行判断
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
    }

第三个方式:yml 配置文件的方式获取规则

    name: "rule4"
    description: "商品的规则 4"
    condition: "product.prices >= 600"
    actions:
      - "System.out.println(\"商品价格大于 600,优惠 100 元\");"

main 方法测试:

    public static void main(String[] args) throws Exception {
        //读取规则 yml 配置文件
        MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        Rule rule04 = ruleFactory.createRule(new FileReader("D:\\work\\rules\\src\\main\\resources\\rules\\rule4.yml"));
        //创建 fact
        Product product = new Product();
        product.setPrices(new BigDecimal(600));
        product.setProductName("裤子");

        Facts facts = new Facts();
        facts.put("product", product);

        //创建规则
        Rules rules = new Rules();
        rules.register(rule04);

        //执行器执行规则
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
    }

手把手写 Demo 使用 Easy Rules 规则引擎

项目目录结构如下:主要是规则引擎工具类、实体 fact 类,以及规则目录。

 

第一步,采用 Spring 初始化项目的方式,创建一个 Spring Boot 项目,增加 Maven 依赖。

Maven 依赖:

    <dependency>
       <groupId>org.jeasy</groupId>
       <artifactId>easy-rules-support</artifactId>
       <version>4.0.0</version>
    </dependency>
    <dependency>
       <groupId>org.jeasy</groupId>
       <artifactId>easy-rules-core</artifactId>
       <version>4.0.0</version>
    </dependency>
    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-simple</artifactId>
       <version>1.7.30</version>
    </dependency>
    <dependency>
       <groupId>org.jeasy</groupId>
       <artifactId>easy-rules-mvel</artifactId>
       <version>4.0.0</version>
    </dependency>

第二步,创建规则引擎工具类,它的目的是读取规则,并实例化。创建规则引擎。

示例代码如下:

    public class RuleEngineUtil {

        /**
         * 创建默认规则引擎执行器
         */
        private static final RulesEngine rulesEngine;

        static {
            RulesEngineParameters parameters = new RulesEngineParameters()
                    //.priorityThreshold(100) //大于指定优先级的规则不执行。
                    .skipOnFirstAppliedRule(true)//默认为 false,如果为 true 从一条开始,只执行匹配成功的第一条。
                    .skipOnFirstFailedRule(true);//默认为 false,如果为 true,规则中抛出异常,就会停止执行后续规则。
                    //.skipOnFirstNonTriggeredRule(true);//默认为 false,如果为 true,从第一条开始匹配,匹配成功才会继续执行后续规则。
            rulesEngine = new DefaultRulesEngine(parameters);
        }

        /**
         * 规则的包路径
         */
        private static final String PACKAGE_NAME = "com.xx.rules.rule";

        /**
         * 初始化规则库


         */
        public static Rules rules = handleRules();

        /**
         * 执行规则判断
         */
        public static void executeEngine(Facts facts){
            //规则引擎执行
            rulesEngine.fire(rules, facts);
        }


        /**
         * 初始化规则库
         */
        private static Rules handleRules(){
            Rules rules = new Rules();
            List<String> list = getRulesNameList();
            for (String name : list) {
                BaseRule baseRule = manualInitialize(name);
                rules.register(baseRule);
            }
            return rules;
        }

        /**
         * 获取所有规则子类的类名
         */
        private static List<String> getRulesNameList() {
            Reflections reflections = new Reflections(PACKAGE_NAME);
            Set<Class<? extends BaseRule>> classes = reflections.getSubTypesOf(BaseRule.class);
            for (Class<? extends BaseRule> config: classes) {
                nameList.add(config.getName());
            }
            return nameList;
        }

        /**
         * 通过反射的方式获取 rule 规则对象
         */
        private static BaseRule manualInitialize(String fullClassName) {
            BaseRule baseRule = null;
            try {
                Class clazz = Class.forName(fullClassName);
                Constructor[] constructors = clazz.getDeclaredConstructors();
                AccessibleObject.setAccessible(constructors, true);
                for (Constructor con : constructors) {
                    if (con.isAccessible()) {
                        baseRule = (BaseRule)con.newInstance();
                        System.out.println(baseRule);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return baseRule;
        }
    }

第三步,创建业务类,也就是 fact 类。

代码如下:

//商品类
public class Product {

    private String productName;

    private BigDecimal prices;

    private BigDecimal res;

    public Product(String productName, BigDecimal prices, BigDecimal res) {
        this.productName = productName;
        this.prices = prices;
        this.res = res;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public BigDecimal getPrices() {
        return prices;
    }

    public void setPrices(BigDecimal prices) {
        this.prices = prices;
    }

    public BigDecimal getRes() {
        return res;
    }

    public void setRes(BigDecimal res) {
        this.res = res;
    }
    }

购物车类:

     /**
         * 购物车
         */
        public class ShoppingCart {

            /**
             * 总结算价格
             */
            private BigDecimal sumPrice = new BigDecimal(0);

            /**
             * 用户
             */
            private User user;

            /**
             * 商品
             */
            private List<Product> productList;

            public BigDecimal getSumPrice() {
                return sumPrice;
            }

            public void setSumPrice(BigDecimal sumPrice) {
                this.sumPrice = sumPrice;
            }

            public User getUser() {
                return user;
            }

            public void setUser(User user) {
                this.user = user;
            }

            public List<Product> getProductList() {
                return productList;
            }

            public void setProductList(List<Product> productList) {
                this.productList = productList;
            }
        }

用户类:

    /**
     * 用户
     */
    public class User {

        private String name;

        private String sex;

        public User(String name, String sex) {
            this.name = name;
            this.sex = sex;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getSex() {
            return sex;
        }

        public void setSex(String sex) {
            this.sex = sex;
        }
    }

第四步,创建规则、规则类。

规则基类:

    public interface BaseRule {

        static final String SHOPPING_CART = "shoppingCart";

        boolean productPrice(@Fact(SHOPPING_CART) ShoppingCart shoppingCart);

        void send(@Fact(SHOPPING_CART) ShoppingCart shoppingCart);
    }

    @Rule(name = "rules01", description = "1 号规则-男士,商品数量满 3 件,总价减少 20 元",priority= 1)
    @Component
    public class BaseRule01 implements BaseRule {

        /**
         * 判断条件是否满足
         */
        @Condition
        public boolean productPrice(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            int size = shoppingCart.getProductList().size();
            User user = shoppingCart.getUser();
            return size >= 3 && user.getSex().equals("男");
        }

        /**
         * 执行行动
         */
        @Action
        public void send(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            System.out.println("执行了-1 号规则-男士,商品数量满 3 件,总价减少 20 元");
            List<Product> productList = shoppingCart.getProductList();
            BigDecimal sum = new BigDecimal(0);
            for(Product product : productList){
                sum = sum.add(product.getPrices());
            }
            shoppingCart.setSumPrice(sum);
            BigDecimal sumPrice = shoppingCart.getSumPrice();
            shoppingCart.setSumPrice(sumPrice.subtract(new BigDecimal(20)));
        }
    }

    @Rule(name = "rules02", description = "2 号规则-女士,商品数量满 4 件,总价减少 40 元",priority= 2)
    @Component
    public class BaseRule02 implements BaseRule {

        /**
         * 判断条件是否满足
         */
        @Condition
        public boolean productPrice(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            int size = shoppingCart.getProductList().size();
            return size >= 4 && shoppingCart.getUser().getSex().equals("女");
        }

        /**
         * 执行行动
         */
        @Action
        public void send(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            System.out.println("执行了-2 号规则-女士,商品数量满 4 件,总价减少 40 元");
            List<Product> productList = shoppingCart.getProductList();
            BigDecimal sum = new BigDecimal(0);
            for(Product product : productList){
                sum = sum.add(product.getPrices());
            }
            shoppingCart.setSumPrice(sum);
            BigDecimal sumPrice = shoppingCart.getSumPrice();
            shoppingCart.setSumPrice(sumPrice.subtract(new BigDecimal(20)));
        }
    }

    @Rule(name = "rules03", description = "3 号规则-女士商品价格满 500 元,赠送 60 元购物券",priority= 3)
    @Component
    public class BaseRule03 implements BaseRule {

        /**
         * 判断条件是否满足
         */
        @Condition
        public boolean productPrice(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            List<Product> productList = shoppingCart.getProductList();
            BigDecimal sum = new BigDecimal(0);
            for(Product product : productList){
                sum = sum.add(product.getPrices());
            }
            shoppingCart.setSumPrice(sum);
            BigDecimal sumPrice = shoppingCart.getSumPrice();
            return sumPrice.doubleValue() >= 500 && shoppingCart.getUser().getSex().equals("女");
        }

        /**
         * 执行行动
         */
        @Action
        public void send(@Fact(SHOPPING_CART) ShoppingCart shoppingCart) {
            System.out.println("执行了-3 号规则-女士商品价格满 500 元,赠送 60 元购物券");
            List<Product> productList = shoppingCart.getProductList();
            BigDecimal sum = new BigDecimal(0);
            for(Product product : productList){
                sum = sum.add(product.getPrices());
            }
            shoppingCart.setSumPrice(sum);
            BigDecimal sumPrice = shoppingCart.getSumPrice();
            shoppingCart.setSumPrice(sumPrice.subtract(new BigDecimal(60)));
        }
        }

最后,执行测试。

测试类:

    public class TestRules {
        public static void main(String[] args) throws Exception {
            ShoppingCart shoppingCart= mockData01();
            Facts facts = new Facts();
            facts.put(BaseRule.SHOPPING_CART, shoppingCart);
            RuleEngineUtil.executeEngine(facts);
            System.out.println("规则处理后:"+shoppingCart.getSumPrice());
        }

        /**
         * 模拟数据 1
         */
        public static ShoppingCart mockData01(){
            ShoppingCart shoppingCart = new ShoppingCart();
            //购物车添加商品
            List<Product> productList = new ArrayList<>();
            Product product = new Product("裤子",new BigDecimal(200),new BigDecimal(0));
            Product product1 = new Product("鞋子",new BigDecimal(300),new BigDecimal(0));
            Product product2 = new Product("衣服",new BigDecimal(400),new BigDecimal(0));
            Product product3 = new Product("裙子",new BigDecimal(200),new BigDecimal(0));
            productList.add(product);
            productList.add(product1);
            productList.add(product2);
            productList.add(product3);
            shoppingCart.setProductList(productList);
            //设置用户为 李三
            User user = new User("李三", "女");
            shoppingCart.setUser(user);
            return shoppingCart;
        }

        /**
         * 模拟数据 1
         */
        public static ShoppingCart mockData02(){
            ShoppingCart shoppingCart = new ShoppingCart();
            //购物车添加商品
            List<Product> productList = new ArrayList<>();
            Product product = new Product("裤子",new BigDecimal(200),new BigDecimal(0));
            Product product1 = new Product("鞋子",new BigDecimal(300),new BigDecimal(0));
            Product product3 = new Product("裙子",new BigDecimal(200),new BigDecimal(0));
            productList.add(product);
            productList.add(product1);
            productList.add(product3);
            shoppingCart.setProductList(productList);
            //设置用户为 李三
            User user = new User("李四", "男");
            shoppingCart.setUser(user);
            return shoppingCart;
        }
    }

执行结果

先会打印规则信息、参数,运行结果:

    17:12:22.661 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Engine parameters { skipOnFirstAppliedRule = true, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = true, priorityThreshold = 2147483647 }
    17:12:22.661 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Registered rules:
    17:12:22.662 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule { name = 'rules01', description = '1 号规则-男士,商品数量满 3 件,总价减少 20 元', priority = '1'}
    17:12:22.664 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule { name = 'rules02', description = '2 号规则-女士,商品数量满 4 件,总价减少 40 元', priority = '2'}
    17:12:22.664 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule { name = 'rules01', description = '3 号规则-女士商品价格满 500 元,赠送 60 元购物券', priority = '3'}
    17:12:22.664 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Known facts:
    17:12:22.664 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Fact{name='shoppingCart', value=com.xx.rules.entity.ShoppingCart@48eff760}
    17:12:22.664 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rules evaluation started
    17:12:22.667 [main] DEBUG org.jeasy.rules.core.DefaultRulesEngine - Rule 'rules01' triggered
    执行了-1 号规则-男士,商品数量满 3 件,总价减少 20 元
    规则处理后:680

总结

相比于 Drools 来说,Easy Rules 的规则相对简单。在项目中,我也喜欢简单直接的利用 注解的方式来创建规则。项目的目录里面,我也会统一一个目录存放规则文件。

这样做的目的,将业务规则统一抽象出来,统一管理。 当产品要求我更换规则,我就不需要从复杂的 Service 层里面去寻找 if else。上万行的代码去找某一个逻辑,难度很大,而且还容易出错。

为了代码的安全,为了逻辑的清晰,为了维护人员的脑细胞。建议大家复杂的逻辑,都采用规则引擎的方式来实现,体验它的魅力。

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站不拥有所有权,不承担相关法律责任。如发现有侵权/违规的内容, 联系QQ15101117,本站将立刻清除。

联系我们

在线咨询:点击这里给我发消息

微信号:666666