本文首发于公众号:托尼学长,立个写 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:是一款国产规则引擎,有开源和商业化两个版本,后者具备更多的功能,提供可视化规则设计界面,对中文支持友好,符合信创要求。
综合对比如下:
| 维度 | Drools | Easy Rules | URule |
|---|---|---|---|
| 定位 | 功能全面的企业级规则引擎 | 轻量级、入门友好的规则引擎 | 国产可视化规则引擎 |
| 是否开源 | 开源 | 开源 | 开源 + 商业版 |
| 语言支持 | Java | Java | Java |
| 社区活跃度 | 高(长期维护,用户基数大) | 中等(核心功能稳定,更新较少) | 中等(国内用户较多) |
| 性能 | 复杂规则计算性能高 | 简单规则计算性能高 | 未知 |
用一句话来说技术选型,那就是业务复杂、对规则引擎功能要求较高的系统,可以无脑选择Drools规则引擎。
业务简单的中小型业务系统,核心诉求是上手快,就选择Easy Rules规则引擎。
有信创国产化要求、需要软件方提供7×24小时运维支持的系统,可以选择URule规则引擎。
另外,各个互联网大厂用以上三种规则引擎不多,通常会选择自研的方式,这也不完全是“重复造轮子”,有如下两方面原因:
1、通用型规则引擎追求“大而全”,自研规则引擎追求根据自身业务“定制化”,能够给予业务更好的支持。
2、熟悉度、可控度高,出现问题更容易排查解决,也更容易优化高并发场景下的性能瓶颈。
落地实现
说到这里,很多同学对于规则引擎仍然有陌生感,毕竟还没看到如何进行代码实现,接下来我们就分别用Drools和Easy Rules各实现一组规则代码。
这两组代码都围绕同一业务场景:“当订单金额 ≥ 1000 且用户为 VIP 时,给予 9 折折扣”。
Drools
- 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);