关于规则引擎,看这一篇就够了

298 阅读6分钟

本文首发于公众号:托尼学长,立个写 1024 篇原创技术面试文章的flag,欢迎过来视察监督~

我们在进行业务系统开发的时候,有没有遇到过这样的场景,系统代码中包含着大量的if else条件判定,且经常会随着业务需求的变更而修改。

举个例子,电商平台的618购物节,运营人员要对系统中的沉睡用户发优惠券进行激活,此时要对沉睡用户人群进行圈选。

最初运营人员制定的用户圈选规则为:一年内没有在平台下单过的用户。

几天后,运营人员希望将规则变更为:一年内没有在平台下单过的用户,或者半年内没有登录过平台的用户。

没过几天,运营人员希望将规则在此变更为:半年内没有在平台下单过的用户,或者三个月内没有登录过平台的用户。

此时工程师一定烦不胜烦,心想:你TMD怎么总变来变去,到底有完没完啊?

这就是典型规则引擎的应用场景了,工程师不再随着运营人员需求的变更而修改代码,而是给他们一套通用的可配置化的页面,让他们自己去配置。

核心概念

规则引擎(Rule Engine)是一种将业务决策逻辑从应用程序代码中剥离出来、并基于预定义规则自动执行决策的软件系统。

用一句话来说,规则引擎 = “把 if...else 写成可热插拔的配置”,让业务人员改规则而不用改代码。

规则配置页面如下图所示:

接下来我们用一张图,来抽象一下规则引擎的构成部分。

我们还是用上文中“电商平台的618购物节,运营人员要对系统中的沉睡用户发放优惠券进行激活”的场景,带入到规则引擎的各个构成部分。

其中沉睡用户的圈选规则为:半年内没有在平台下单过的用户,或者三个月内没有登录过平台的用户,并为其发送618购物节的优惠券。

表达式1:(特征)用户最后下单时间 (运算符)< (阈值)当前日期减六个月

表达式2:(特征)用户最后登录时间 (运算符)< (阈值)当前日期减三个月

逻辑关系:或。

执行动作:发放优惠券。

另外,在规则之上还有个“规则组”的概念,这样可以把一系列的规则放在一起,以便于管理。

适用场景及优点

规则引擎适用于业务变更频繁、强是非判定(true | false)的业务场景,包括:电商促销、金融风控、银行反欺诈、IoT告警等。

其核心优点包括三个:

1、功能即时生效,规则配置支持热更新,无需再走整体的代码修改、功能测试、发布上线等流程。

2、节省研发资源,通过可视化配置页面,运营人员或技术支持即可完成规则更新,无需研发人员参与。

3、提升系统可维护性,分离系统中变与不变的部分,各自专注内聚,有利于系统问题定位。

当然,规则引擎并不是一个零门槛、简单易学的东西,需要工程师具备一定的抽象思维能力。

选型对比

接下来我们来盘点一下市面上主流的几个规则引擎,看看它们各自的特点和优缺点。

Drools:最知名、功能最为强大的开源规则引擎之一,支持复杂的规则逻辑和动态规则管理,广泛应用于金融、保险等领域。Easy Rules:轻量级开源规则引擎,API简洁易用,适合规则不复杂的中小型项目,是基于POJO的开发与注解的编程模型。URule:是一款国产规则引擎,有开源和商业化两个版本,后者具备更多的功能,提供可视化规则设计界面,对中文支持友好,符合信创要求。

综合对比如下:

维度DroolsEasy RulesURule
定位功能全面的企业级规则引擎轻量级、入门友好的规则引擎国产可视化规则引擎
是否开源开源开源开源 + 商业版
语言支持JavaJavaJava
社区活跃度高(长期维护,用户基数大)中等(核心功能稳定,更新较少)中等(国内用户较多)
性能复杂规则计算性能高简单规则计算性能高未知

用一句话来说技术选型,那就是业务复杂、对规则引擎功能要求较高的系统,可以无脑选择Drools规则引擎。

业务简单的中小型业务系统,核心诉求是上手快,就选择Easy Rules规则引擎。

有信创国产化要求、需要软件方提供7×24小时运维支持的系统,可以选择URule规则引擎。

另外,各个互联网大厂用以上三种规则引擎不多,通常会选择自研的方式,这也不完全是“重复造轮子”,有如下两方面原因:

1、通用型规则引擎追求“大而全”,自研规则引擎追求根据自身业务“定制化”,能够给予业务更好的支持。

2、熟悉度、可控度高,出现问题更容易排查解决,也更容易优化高并发场景下的性能瓶颈。

落地实现

说到这里,很多同学对于规则引擎仍然有陌生感,毕竟还没看到如何进行代码实现,接下来我们就分别用Drools和Easy Rules各实现一组规则代码。

这两组代码都围绕同一业务场景:“当订单金额 ≥ 1000 且用户为 VIP 时,给予 9 折折扣”。

Drools

  1. Drools规则配置(DRL 文件)
// src/main/resources/discount.drl
package com.example.rules
import com.example.Order
rule "VIP big order discount"
    when
        $o : Order(amount >= 1000, vip == true)
    then
        $o.setDiscount(0.9);
        System.out.println("Drools: VIP 9折已应用");
end

2、Java启动代码

KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
KieSession kSession = kc.newKieSession("discountKS");
Order order = new Order(1200, true);
kSession.insert(order); 
kSession.fireAllRules();
kSession.dispose();

怎么样,是不是很容易理解?如果这也觉得难得话,接下来我们看下Easy Rules的代码实现。

Easy Rules

1、Easy Rules规则配置(纯Java代码)

@Rule(name = "VIP big order discount")
public class VipDiscountRule {
    @Condition
    public boolean isVIPBigOrder(@Fact("order") Order o) {
        return o.getAmount() >= 1000 && o.isVip();
    }
    @Action
    public void applyDiscount(@Fact("order") Order o) {
        o.setDiscount(0.9);
        System.out.println("EasyRules: VIP 9折已应用");
    }
}

2、Java启动代码

RulesEngine engine = new DefaultRulesEngine();
Order order = new Order(1200, true);
Facts facts = new Facts();
facts.put("order", order);
engine.fire(new Rules(new VipDiscountRule()), facts);