java规则引擎Aviator

在灰度系列中《基于springcloud的灰度实现方案(二)》,之前规则适配使用数据库+策略模式实现,单个规则还好,多个规则,各种场景使用,还是稍微有点欠缺。就想着用java规则引擎来解决这个问题。

之前在项目中使用过drools,比较重,初始加载复杂,首次执行效率较低,最好预热一下,其次分布式规则处理时的一致性也得自己把控;

之前就了解过aviator,这次就直接用了。

相关资料

# 官网地址
https://github.com/killme2008/aviator
# 开发文档
https://www.yuque.com/boyan-avfmj/aviatorscript/cpow90

简介:

在5.0之前aviator只是一个表达式引擎主要用于各
种表达式的动态求值。
到5.0以后,aviator 变成了一门通用的脚本语言aviatorScript.
- 直接翻译成对应的java字节码;
- 高性能(最多扫两套)
- 轻量级,只依赖commons-beanutils这个库的反射,整个jar到5.0页就430k;
相对有特色的点:
- 支持运算重载;
- 原生支持大整数和BigDecimal类型及运算;
- 原生支持正则表达式类型及匹配运算符 =~;
- 类clojure的seq库及lambda支持可以灵活地处理各种集合
- 开放能力:包括自定义函数接入以及各种定制选项


ps: 如果用不到5.0以后的功能,使用之前的版本即可,之前的包只有几十kb;

特性:

  • 词法作用域 {…} ,和 let 定义作用域内的变量

  • return 语句,用于从函数或者 script 中返回(值)。

  • if/elsif/else 条件语句

  • for/while 循环语句,以及 break / continue 支持

  • fn 语法用于定义命名函数, 4.0 已经引入了 lambda -> … end语法专门用于匿名函数定义

  • 单行注释 支持

  • 和 Java Scripting API 更好的集成

  • 字符串插值

  • 异常处理 try…catch…finally 语句等等。

项目中使用

        <dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>5.2.5</version>
        </dependency>

示例:比如,在灰度规则里 手机尾号为9和0,用户id>901,注册时间大于‘2021-06-25’ 的用户进入灰度环境 硬编码好说,策略也还好,但是如果加上或运算,策略也不好处理, 表达式最好了

    @Test
    public void grayRule(){
        Map<String,Object> map = new HashMap<>();
        final Date date = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(date);
        map.put("mobileTail","9");
        map.put("userId",901);
        map.put("registerTime",dateStr);
        map.put("mobile","15600000269");
        map.put("sex",1);
        map.put("age",28);
        // 手机尾号
        String expression = "(string.startsWith('9,0',mobileTail) && userId>=901 && registerTime>'2021-06-25 00:00:00')";
        Boolean flag = (Boolean) AviatorEvaluator.execute(expression, map);
        Assert.assertTrue("规则验证通过",flag);
    }

用了以后,操作真简单。

执行方式一:直接表达式+参数

AviatorEvaluator.execute() 
或
AviatorEvaluator.getInstance().execute()

最终底层:
AviatorEvaluatorInstance.execute()

script脚本+ 参数


执行方式二:script脚本+参数

script脚本

str = "";
if (age <=1 ){
  str = "婴儿";
} elsif (age>1 && age<=6) {
   str = "儿童";
} elsif (age>6 && age<=17) {
  str = "青少年";
} elsif (age>18 && age<=40){
  str = "青年";
} elsif (age>40 && age<=48){
  str = "壮年";
} elsif (age>48 && age<=65){
  str = "中年";
} elsif (age>65){
  str = "老年";
}
return str="#{name}处在#{str}期";

@Test
public void script2() throws IOException {
    //编译脚本
    //路径是文件系统的绝对路径或相对路径,
    //相对路径的时候,必须项目的根目录开始的相对路径
    //classpath下的绝对或相对路径
    Expression compiledExp = AviatorEvaluator.getInstance().compileScript("src/test/resources/script.av");
    //执行脚本,参数可以map,也可以通过newEnv kv对的方式塞入,最终还是map
    final Object o = compiledExp.execute(compiledExp.newEnv("age", 12, "name", "yxk"));
    System.out.println(o);
}

输出结果

> yxk处在青少年期

#### 内置函数

aviator内置了很多函数,具体可以看官网 https://www.yuque.com/boyan-avfmj/aviatorscript/ashevw

比如:上面我们用的string.startsWith(‘9,0’,mobileTail)

@Test
public void function_in(){
    long num =(Long) AviatorEvaluator.getInstance().execute("math.round(4.3)");
    Assert.assertEquals("4.3四舍五入后", 4L ,num);
    Map<String,Object> map = new HashMap<>();
    map.put("str","yxkong");
    map.put("head","yxk");
    Boolean flag = (Boolean) AviatorEvaluator.getInstance().execute("string.contains(str,head)",map);
    Assert.assertTrue("yxkong包含yxk",flag);
}

#### 自定义函数

//定义函数
class AddFunction extends AbstractFunction {
@Override
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {

    long num1 = FunctionUtils.getNumberValue(arg1, env).longValue();
    long num2 = FunctionUtils.getNumberValue(arg2, env).longValue();
    return AviatorLong.valueOf(num1+num2);
}

@Override
public String getName() {
    return "add";
}

}

@Test
public void function_my(){
    // 注册自定义函数
    AviatorEvaluator.addFunction(new AddFunction());
    long num =(Long) AviatorEvaluator.getInstance().execute("add(3,4)");
    Assert.assertEquals("3+4", 7L,num);
}

![公众号图片](https://img-blog.csdnimg.cn/20210407102553361.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Y4MDQwNzUxNQ==,size_16,color_FFFFFF,t_70#pic_center)

Guess you like

Origin blog.csdn.net/f80407515/article/details/118487545