规则引擎技术在转转钱包的实践

开发 项目管理
使用while循环来保持程序持续运行,用以判断数字大小并记录猜测次数。这是我们常采用的命令式编程方式:明确地指定每个步骤的执行顺序和详细的操作细节,例如变量的赋值、条件判断、循环控制等。

1.什么是规则引擎和命令式编程

让我们先来看一个有趣的猜数字小游戏:玩家需要猜测一个1到100之间的随机数字。每次猜测后,系统会提示玩家所猜的数字是大于还是小于随机数,玩家需要根据提示继续猜测,直到猜中为止。

// 生成一个1到100之间的随机整数
secret = random(1, 100)
// 初始化猜测次数为0
guesses = 0
// 循环猜数字
while true do
// 读取用户输入的整数
guess = input("Guess a number between 1 and 100: ")
guesses = guesses + 1
// 判断猜测结果
if guess < secret then
    print("Too low, try again.")
  else if guess > secret then
    print("Too high, try again.")
  else
    print("you guessed it in", guesses, "guesses!")
    break
  end if
end while

使用while循环来保持程序持续运行,用以判断数字大小并记录猜测次数。这是我们常采用的命令式编程方式:明确地指定每个步骤的执行顺序和详细的操作细节,例如变量的赋值、条件判断、循环控制等。

再来看下规则引擎编程方式:

// 定义规则1
rule "Guess a number"
when
  $guess: Integer()
  $secret: Integer(intValue > $guess) 
  then
  System.out.println("Too low, try again.");
end
 //定义规则2
rule "Guess a number"
when
  $guess: Integer()
  $secret: Integer(intValue < $guess) 
  then
  System.out.println("Too high, try again.");
end
 //定义规则3
rule "Guess a number"
when
  $guess: Integer()
  $secret: Integer(intValue == $guess)
then
  System.out.println("you guessed it!");
end

上述代码定义了3条规则,每条规则都包含执行条件(when语句)和动作(then语句)。其中,规则1指定:当输入的数字小于initValue时,应打印 “Too low, try again.”。规则引擎编程方式是:将具体的代码逻辑抽象为对应的业务规则,并通过这些规则的定义和执行来实现。

规则引擎编程价值

当我们能够将业务逻辑代码抽象为相应的业务规则时,业务人员就可以通过修改规则的条件和动作来快速迭代业务逻辑。这正是规则引擎的第一个价值:业务具有高度的可扩展性。

规则引擎的另一个价值是:项目具有高度的可维护性。与上述命令式编程方式实现的小游戏代码相比,多个if-else语句不仅增加了代码的复杂度和维护成本,还易导致代码的可读性和可维护性降低。而规则引擎方式使业务流程更加清晰和直观,降低应用程序的耦合度,并在一定程度上实现业务与技术的分离。

总之,规则引擎是一种更高级的条件判断手段。它通过规则的方式来决定行为,使用简单的规则语言来表达复杂的业务逻辑,并具有更好的业务可扩展性和项目可维护性。

2.规则引擎在转转钱包的应用

转转钱包是一个有温度的金融钱包。在这里,可以参与免息分期购物活动,使用安全快捷的小额借贷服务,甚至可以1元租用高端手机。欢迎大家来体验和使用。

图片

转转钱包

在最近对“我的钱包”进行的改版中,业务同学提出需求:根据各个用户当前的业务状态展示相应的分期、借钱以及租赁的卡片内容和页面跳转路径。

图片

如上图所示的需求中,借钱卡片包含7种场景,分期卡片包含5种场景,手机租赁包含3种场景。如果按照常规的命令式编程方式:

  1. 代码中将包含大量的if-else语句,可维护性会变差
  2. 一旦业务方想要调整某状态下的交互行为,需要修改代码并重新发版

规则引擎在执行前,需要计算所有用户的业务状态,而在某些场景下,命令式编程可能无需计算所有业务状态就可以得出结果,这可以在一定程度上提高性能。在权衡利弊后,我们决定在转转钱包中采用规则引擎,因为其优点远大于缺点。

规则建模

在使用规则引擎之前,有一个关键点需要充分考量:是否可以构建一个良好的规则模型。一个好的规则模型可以使规则系统更易于理解、维护和扩展。比如上文提到的借钱卡片状态,我们可以抽象出以下规则:账户是否停用、是否新户、是否可以申请贷款、是否有额度。找到这些规则条件后,我们可以反过来检查这些规则是否可以覆盖所有的状态描述,以避免业务场景有遗漏。简言之,我们要找出业务逻辑中共性的规则条件,然后使用这些条件来倒推校验业务逻辑的完整性。

选择引擎组件

你可以自己构建一个简单的规则引擎。只需要创建一组带有条件和操作的对象,将它们存储在合适的集合中,然后遍历这些对象来评估条件和执行操作。当然,我们没有必要重新造轮子,市面上已经有几个常用的规则引擎组件,例如:drools、easy-rules、aviator和liteFlow等。大家可以根据自己的业务场景选择合适的组件。转转钱包选择了easy-rules,因为在满足业务需求的基础上,它短小强悍。

整体设计

如下图所示,我们将规则配置在Apollo中以实现动态调整。高效地计算每个用户的分期、借钱和租赁状态,再将规则集和相关事实输入到规则引擎中,最后得到各卡片的结果状态。在此过程中,可能会有以下疑问:该规则引擎的执行效率如何?它又是如何评估规则的?带着这些疑问,让我们来看看规则引擎的源码实现。

图片

3.EasyRules性能分析

本节将通过阅读easy-rules规则引擎中与规则评估和执行相关的源码,来了解其效率水平

图片

支持MVEL和Spel表达式

规则评估

通过查看org.jeasy.rules.core.DefaultRulesEngine#fire方法,我们进入到doFire() 方法里

void doFire(Rules rules, Facts facts) {
  for (Rule rule : rules) {   //遍历规则
    boolean evaluationResult = false;
    evaluationResult = rule.evaluate(facts); //评估规则条件是否成立
    if (evaluationResult) {
        rule.execute(facts);  //如果成立,执行规则的动作
            }
        }
    }

上面代码只保留了主要逻辑,规则评估通过for循环遍历规则集,逐一评估每个规则的条件是否满足,如果条件满足则执行相应的动作。但是,如果您的规则量非常大,此规则引擎组件可能不是最佳选择。这时可以考虑使用高效的Rete规则匹配算法。Rete算法巧妙地利用了规则之间的关联关系,构建一个高效的规则匹配网络。当有新事实进入时,它可以高效地匹配该事实与已有规则的匹配情况。

规则执行

protected Rule createSimpleRule(RuleDefinition ruleDefinition) {
    MVELRule mvelRule = new MVELRule(parserContext)
        .name(ruleDefinition.getName())
        .priority(ruleDefinition.getPriority())
        .when(ruleDefinition.getCondition());  //步骤1
        for (String action : ruleDefinition.getActions()) {
            mvelRule.then(action);  //步骤2
        }
        return mvelRule;
    }
     
public MVELAction(String expression, ParserContext parserContext) {
    this.expression = expression;
    compiledExpression = MVEL.compileExpression(expression, parserContext);  // 使用mvel编译规则 
}

步骤1和步骤2在创建规则时,easy-rules利用MVEL或SpEL表达式语言的能力,提前编译规则的条件表达式(condition)和动作表达式(action)。因此,规则的执行效率非常高。

这一点在我们准备618大促的压力测试数据中也得以体现。测试结果显示,即使峰值QPS达到1.5万,响应时间的最大值也仅为10.3ms

图片

qps

图片

响应时间


关于作者:
李文,转转金融技术部研发工程师

责任编辑:武晓燕 来源: 转转技术
相关推荐

2023-07-12 08:33:34

引擎LiteFlow编排

2023-03-22 08:32:35

2022-10-28 09:15:02

2022-10-28 08:31:43

2022-11-02 09:02:08

Drools引擎DMN

2023-04-19 13:18:41

动态线程池平台

2023-09-14 08:34:28

linux架构参数

2023-08-24 08:11:39

断路器监控报警

2024-09-11 19:36:24

2024-10-16 21:49:24

2022-04-06 15:58:25

火山引擎差分隐私LDPDC

2024-09-19 22:22:41

多任务学习

2023-11-01 07:44:29

转转Flutter业务

2022-11-07 14:45:26

转转价格DDD

2023-12-27 19:12:42

OLAP自助分析

2022-12-21 08:32:34

OLAPDruid架构

2023-03-02 08:54:32

2023-03-02 08:32:41

2023-05-31 14:54:32

2023-02-08 09:42:30

策略方式容量
点赞
收藏

51CTO技术栈公众号