2

风控规则引擎(一):Java 动态脚本 - 双鬼带单

 6 months ago
source link: https://www.cnblogs.com/zyndev/p/18072361
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

风控规则引擎(一):Java 动态脚本

  1. 共享单车会根据微信分或者芝麻分来判断是否交押金
  2. 汽车租赁公司也会根据微信分或者芝麻分来判断是否交押金
  3. 在一些外卖 APP 都会提供根据你的信用等级来发放贷款产品
  4. 金融 APP 中会根据很复杂规则来判断用户是否有借款资格,以及贷款金额。

在简单的场景中,我们可以通过直接编写一些代码来解决需求,比如:

// 判断是否需要支付押金return 芝麻分 > 650

这种方式代码简单,如果规则简单且不经常变化可以通过这种方式,在业务改变的时候,重新编写代码即可。

在金融场景中,往往会根据不同的产品,不同的时间,对接的银行等等多个维度来配置规则,单纯的直接编写代码无法满足业务需求,而且编写代码的方式对于运营人员来说无论实时性、可视化都很欠缺。

在这种情况往往会引入可视化的规则引擎,允许运营人员可以通过可视化配置的方式来实现一套规则配置,具有实时生效、可视化的效果。减少开发和运营的双重负担。

这篇主要介绍一下如何实现一个可视化的表达式的定义和执行。

表达式的定义

在上面说到的使用场景中,可以了解中至少需要支持布尔表达式。比如

  1. 芝麻分 > 650
  2. 居住地 不在 国外
  3. 年龄在 18 到 60 之间
  4. 名下无其他逾期借款

在上面的例子中,可以将一个表达式分为 3 个部分

  1. 规则参数 (ruleParam)
  2. 对应的操作 (operator)
  3. 对应操作的阈值 (args)

则可以将上面的布尔表达式表示为

  1. 芝麻分 > 650
{ "ruleParam": "芝麻分", "operator": "大于", "args": ["650"]}
  1. 居住地 不在 国外
{ "ruleParam": "居住地", "operator": "位于", "args": ["国内"]}
  1. 年龄在 18 到 60 之间
{ "ruleParam": "年龄", "operator": "区间", "args": ["18", "60"]}
  1. 名下无其他逾期借款
{ "ruleParam": "在途逾期数量", "operator": "等于", "args": ["0"]}

表达式执行

上面的通过将表达式使用 json 格式定义出来,下面就是如何在运行中动态的解析这个 json 格式并执行。

有了 json 格式,可以通过以下方式来执行对应的表达式

  1. 因为表达式的结构已经定义好了,可以通过手写代码来判断所有的情况实现解释执行, 这种方案简单,但增加操作需要修改对应的解释的逻辑, 且性能低
/*{ "ruleParam": "在途逾期数量", "operator": "等于", "args": ["0"]}*/switch(operator) { case "等于": // 等于操作 break; case "大于": // 等于操作 break; ...}
  1. 在第一次得到 json 字符串的时候,直接将其根据不同的情况生成对应的 java 代码,并动态编译成 Java Class,方便下一次执行,该方案依然需要处理各种情况,但因为在第一次编译成了 java 代码,性能和直接编写 java 代码一样

  2. 使用第三方库实现表达式的执行

使用第三方库实现动态表达式的执行

在 Java 中有很多表达式引擎,常见的有

  1. jexl3
  2. spring-expression
  3. QLExpress
  4. groovy
  5. aviator

这里简单介绍一下 jexl3 和 aviator 的使用

jexl3 在 apache commons-jexl3 中,该表达式引擎比较符合人的书写习惯,其会判断操作的类型,并将参数转换成对应的类型比如 3 > 4 和 "3" > 4 这两个的执行结果是一样的

aviator 是一个高性能的 Java 的表达式类型,其要求确定参数的类型,比如上面的 "3" > 4 在 aviator 是无法执行的。

jexl3 更适合让运营手动编写的情况,能容忍一些错误情况;aviator 适合开发来使用,使用确定的类型参数来提供性能

jexl3 使用

<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-jexl3</artifactId> <version>3.2.1</version></dependency>
// 创建一个带有缓存 jexl 表达式引擎,JexlEngine JEXL = new JexlBuilder().cache(1000).strict(true).create(); // 根据表达式字符串来创建一个关于年龄的规则JexlExpression ageExpression = JEXL.createExpression("age > 18 && age < 60"); // 获取需要的参数,java 代码太长了,简写一下Map<String, Object> parameters parameters = {"age": 30} // 执行一下JexlContext jexlContext = new MapContext(parameters); boolean result = (boolean) executeExpression.evaluate(jexlContext);

以上就会 jexl3 的简单使用

aviator

<dependency> <groupId>com.googlecode.aviator</groupId> <artifactId>aviator</artifactId> <version>5.3.1</version></dependency>
Expression ageExpression = executeExpression = AviatorEvaluator.compile("age > 18 && age < 60"); // 获取需要的参数,java 代码太长了,简写一下Map<String, Object> parameters parameters = {"age": 30} boolean result = (boolean) ageExpression.execute(parameters);

注意 aviator 是强类型的,需要注意传入 age 的类型,如果 age 是字符串类型需要进行类型转换

不同表达式引擎的性能测试

Benchmark Mode Cnt Score Error UnitsEmpty thrpt 3 1265642062.921 ± 142133136.281 ops/sJava thrpt 3 22225354.763 ± 12062844.831 ops/sJavaClass thrpt 3 21878714.150 ± 2544279.558 ops/sJavaDynamicClass thrpt 3 18911730.698 ± 30559558.758 ops/sGroovyClass thrpt 3 10036761.622 ± 184778.709 ops/sAviator thrpt 3 2871064.474 ± 1292098.445 ops/sMvel thrpt 3 2400852.254 ± 12868.642 ops/sJSEL thrpt 3 1570590.250 ± 24787.535 ops/sJexl thrpt 3 1121486.972 ± 76890.380 ops/sOGNL thrpt 3 776457.762 ± 110618.929 ops/sQLExpress thrpt 3 385962.847 ± 3031.776 ops/sSpEL thrpt 3 245545.439 ± 11896.161 ops/sFel thrpt 3 21520.546 ± 16429.340 ops/sGroovyScript thrpt 3 91.827 ± 106.860 ops/s

这是写的规则引擎的第一篇,主要讲一下

  1. 如何讲一个布尔表达式转换为 json 格式的定义方便做可视化存储和后端校验
  2. 如何去执行一个 json 格式的表达式定义

在这里也提供了一些不同的表达式引擎和性能测试,如果感兴趣的可以去尝试一下。

下一篇主要讲一下在引擎里面规则参数、操作符是如何设计的,也讲一下可视化圆形的设计


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK