当前位置: 首页 > 工具软件 > Aviator > 使用案例 >

Aviator规则表达式引擎介绍及源码跟踪

杜烨伟
2023-12-01

Aviator

规则表达式引擎分类

  1. 编译型规则表达式引擎:预先编译成可执行对象,运行时多次执行;
  2. 解释型规则表达式引擎:不需要预先进行编译,在运行时,要先进行解释再运行。

Aviator原理及特点

Aviator 的基本过程是将字符串表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-beanutils 这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。因此总结它的特点是:

  • 高性能
  • 轻量级
  • 开放能力:包括自定义函数接入以及各种定制选项
  • 一些比较有特色的特点:
    1.支持运算符重载
    2.原生支持大整数和 BigDecimal 类型及运算,并且通过运算符重载和一般数字类型保持一致的运算方式。
    3.原生支持正则表达式类型及匹配运算符 =~
    4.支持lambda ,可以灵活地处理各种集合

Aviator解析

AviatorScript 编译和执行的入口是 AviatorEvaluatorInstance 类,该类的一个实例就是一个编译和执行的单元。

AviatorEvaluator.getInstance() 返回一个全局共享的AviatorEvaluatorInstance 类,没有定制化的需求,该默认引擎已足够我们本次的讲解。

AviatorEvaluatorInstance 入口常用Api:

public final class AviatorEvaluatorInstance {
  //编译字符串表达式,cacheKey缓存标识,cached-是否缓存
  public Expression compile(final String cacheKey, final String expression, final boolean cached)
  
  //编译aviatorScript脚本文件,cacheKey缓存标识,cached-是否缓存
  public Expression compileScript(final String cacheKey, final File file, final boolean cached) throws IOException
  
  //执行字符串表达式,env环境变量,cached-是否缓存Expression对象
  public Object execute(final String expression, final Map<String, Object> env,
      final boolean cached)      
}

示例1、执行字符串表达式

AviatorEvaluatorInstance aviatorEvaluatorInstance = AviatorEvaluator.newInstance();
//1.直接执行表达式
String exp1 = "1+2+3";
Object execute = aviatorEvaluatorInstance.execute(exp1);
System.out.println(execute);//6
String exp2 = "100>1000";
Object execute2 = aviatorEvaluatorInstance.execute(exp2);
System.out.println(execute2);//false
//2.使用环境变量
Map<String, Object> env = AviatorEvaluator.newEnv("a", 100, "b", 200, "c", 300);
String exp3 = "a+b+c";
Object execute3 = aviatorEvaluatorInstance.execute(exp3,env);
System.out.println(execute3);//600
String exp4 = "a+b==c";
Object execute4 = aviatorEvaluatorInstance.execute(exp4,env);
System.out.println(execute4);//false
//访问环境变量数组和集合
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
int[] nums = new int[5];
for(int i=0;i<nums.length;i++){
    nums[i]=i;
}
Map<String,Object> map2 = new HashMap<String,Object>();
map2.put("date","2021-08-21");
Map<String,Object> map3 = new HashMap<String,Object>();
map3.put("list",list);
map3.put("nums",nums);
map3.put("map2",map2);
//hello world
System.out.println(AviatorEvaluator.execute("list[0]+' '+list[1]",map3));
//nums[0]+nums[1]+nums[2]=3
System.out.println(AviatorEvaluator.execute("'nums[0]+nums[1]+nums[2]='+ (nums[0]+nums[1]+nums[2])",map3));
//当前时间为:2021-08-21
System.out.println(AviatorEvaluator.execute("'当前时间为:'+map2.date",map3));
}

示例2、执行/导入函数

AviatorEvaluatorInstance aviatorEvaluatorInstance = AviatorEvaluator.newInstance();
//1.使用自有函数
System.out.println(AviatorEvaluator.execute("string.substring('habcello',1,3)"));//ab
System.out.println(AviatorEvaluator.execute("string.contains(\"abc\",\"ab\")"));//true
//2.导入静态方法
aviatorEvaluatorInstance.addStaticFunctions("StringUtils",StringUtils.class);
System.out.println(aviatorEvaluatorInstance.execute("StringUtils.isBlank('abc')"));//false
//3.导入实例方法
aviatorEvaluatorInstance.addInstanceFunctions("String",String.class);
System.out.println(aviatorEvaluatorInstance.execute("String.indexOf('abc','b')"));//1
//4.Function Missing调用
// 启用基于反射的方法查找和调用
AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance()); 
// 调用 String#indexOf
System.out.println(AviatorEvaluator.execute("indexOf('hello world', 'w')"));

示例3、使用自定义函数

public class TestAviator {
    public static void main(String[] args) {
            //注册函数
            AviatorEvaluator.addFunction(new AddFunction());
            System.out.println(AviatorEvaluator.execute("add(1, 2)"));           // 3.0
            System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0
        }
    }
    class AddFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, 
                                  AviatorObject arg1, AviatorObject arg2) {
            Number left = FunctionUtils.getNumberValue(arg1, env);
            Number right = FunctionUtils.getNumberValue(arg2, env);
            return new AviatorDouble(left.doubleValue() + right.doubleValue());
        }
        public String getName() {
            return "add";
        }
    }

性能执行配置推荐

Aviator具有两种运行模式:
1.执行优先,适合表达式不经常变化,将编译结果缓存并重复运行的场景(默认)
AviatorEvaluator.setOption(Options.OPTIMIZE_LEVEL, AviatorEvaluator.EVAL)
2.编译优先,适合表达式经常变化,不缓存直接运行的场景
AviatorEvaluator.setOption(Options.OPTIMIZE_LEVEL, AviatorEvaluator.COMPILE);
推荐优先使用执行优先模式,使用编译结果缓存模式,复用编译结果,传入不同变量执行

外部变量传入,优先使用编译结果的 Expression#newEnv(…args) 方法创建外部 env,将会启用符号化,降低变量访问开销。

调用 Java 方法,优先使用自定义函数,其次是导入方法,最后是基于 FunctionMissing 的反射模式。

最佳实践

//最佳实践
//编译表达式,并缓存编译对象
Expression abc = aviatorEvaluatorInstance.compile("exp1","a+b+c",true);
//设置环境变量
Map<String, Object> stringObjectMap = abc.newEnv("a", 123, "b", 456, "c", 789);
//执行表达式
Object execute1 = abc.execute(stringObjectMap);
System.out.println(execute1);//1368
//读取缓存
Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey("exp1");
System.out.println(abc == abc2);//true

源码跟踪

我们从最佳实践的流程中跟踪源码的实现方式,主要分为以下的三步:

  1. (编译)Expression abc = aviatorEvaluatorInstance.compile(“exp1”,“a+b+c”,true);
  2. (执行)Object execute1 =abc.execute(stringObjectMap);
  3. (读取缓存)Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey(“exp1”);

编译:

Expression abc = aviatorEvaluatorInstance.compile(“exp1”,“a+b+c”,true);
首先进入compile方法

private Expression compile(final String cacheKey, final String expression,
    final String sourceFile, final boolean cached) {
  /* 忽略检查代码 */
  if (cached) {//如果本次编译需要缓存
    FutureTask<Expression> existedTask = null;
    if (this.expressionLRUCache != null) {//如果开启LRU缓存配置,则缓存入LRU集合
      boolean runTask = false;
      synchronized (this.expressionLRUCache) {//对象加syn锁
        existedTask = this.expressionLRUCache.get(cacheKey);//判断LRU集合是否存在缓存
        if (existedTask == null) {
          //无缓存结果,构建Callable编译任务
          existedTask = newCompileTask(expression, sourceFile, cached);
          runTask = true;
          //将Callable任务存入缓存集合中
          this.expressionLRUCache.put(cacheKey, existedTask);
        }
      }
      if (runTask) {
        existedTask.run();
      }
    } else {//普通缓存模式,存入ConcurrentHashMap集合中
      FutureTask<Expression> task = this.expressionCache.get(cacheKey);
      if (task != null) {
        return getCompiledExpression(expression, task);
      }
      task = newCompileTask(expression, sourceFile, cached);
      existedTask = this.expressionCache.putIfAbsent(cacheKey, task);
      if (existedTask == null) {
        existedTask = task;
        existedTask.run();
      }
    }
    //获取Callable任务的结果,existedTask.get()
    return getCompiledExpression(cacheKey, existedTask);
  } else {//未开启缓存,直接进行编译并返回
    return innerCompile(expression, sourceFile, cached);
  }
}

构建Callable编译任务:newCompileTask实际上也是调用了innerCompile方法,这里我们接着看innerCompile方法

  private FutureTask<Expression> newCompileTask(final String expression, final String sourceFile,final boolean cached) {
    return new FutureTask<>(new Callable<Expression>() {
      @Override
      public Expression call() throws Exception {
        return innerCompile(expression, sourceFile, cached);
      }
    });
  }

innerCompile:

private Expression innerCompile(final String expression, final String sourceFile,
    final boolean cached) {
  //词法分析器,用来对aviator脚本进行词法解析,如将脚本解析为变量、数字、字符串、注释等;
  ExpressionLexer lexer = new ExpressionLexer(this, expression);
  //字节码生成器,用于动态生成自定义的字节码;
  CodeGenerator codeGenerator = newCodeGenerator(sourceFile, cached);
  //表达式解析器,用于将脚本编译为表达式对象(BaseExpression)
  ExpressionParser parser = new ExpressionParser(this, lexer, codeGenerator);
  //通过ASM字节码生成技术生成字节码对象并利用构造函数生成实例对象,这里不进行分析
  Expression exp = parser.parse();
  if (getOptionValue(Options.TRACE_EVAL).bool) {
    ((BaseExpression) exp).setExpression(expression);
  }
  return exp;
}

"a+b+c"表达式生成的字节码对象反编译如下所示

public class SubClassExpression extends ClassExpression {
  private final AviatorJavaType f0;//a
	private final AviatorJavaType f1;//b
	private final AviatorJavaType f2;//c
  public SubClassExpression(final AviatorEvaluatorInstance instance, final List<String> varNames,final SymbolTable symbolTable){
  	super(instance, varNames, symbolTable);
    f0 = new AviatorJavaType("a", symbolTable); 
    f1 = new AviatorJavaType("b", symbolTable); 
    f2 = new AviatorJavaType("c", symbolTable); 
  }
  public final Object execute0(Env env){
  	return f0.add(f1,env).add(f2,env);
  }
}

至此编译动作完成,返回Expression实例对象并加入缓存集合

执行:

Object execute1 =abc.execute(stringObjectMap)
首先看Expression实例对象.execute方法

public Object execute(Map<String, Object> map) {
  if (map == null) {
    map = Collections.emptyMap();
  }
  //环境变量封装
  Env env = genTopEnv(map);
  //环境变量执行器
  EnvProcessor envProcessor = this.instance.getEnvProcessor();
  if (envProcessor != null) {
    envProcessor.beforeExecute(env, this);
  }
  try {
    //执行
    return executeDirectly(env);
  } finally {
    if (envProcessor != null) {
      envProcessor.afterExecute(env, this);
    }
  }
}

接下来看下executeDirectly(Map<String, Object> env)

public Object executeDirectly(final Map<String, Object> env) {
   try {
     //执行ASM生成的字节码实例对象的execute0方法
     Object result = execute0((Env) env);
     if (RuntimeUtils.isTracedEval(env)) {
       RuntimeUtils.printlnTrace(env, "Result : " + result);
     }
     //返回结果,这里是f0.add(f1,env).add(f2,env),123+456+789
     return result;
   } catch (ExpressionRuntimeException e) {
     throw e;
   } catch (Throwable t) {
     throw Reflector.sneakyThrow(t);
   }
 }

读取缓存:

Expression abc2 = aviatorEvaluatorInstance.getCachedExpressionByKey(“exp1”);

 public Expression getCachedExpressionByKey(final String cacheKey) {
   FutureTask<Expression> task = null;
   if (this.expressionLRUCache != null) {//开启了LRU缓存则从LRU缓存中读取
     synchronized (this.expressionLRUCache) {//syn锁,效率不如普通缓存集合
       task = this.expressionLRUCache.get(cacheKey);
     }
   } else {//从普通缓存集合中读取
     task = this.expressionCache.get(cacheKey);
   }
   if (task != null) {
     return getCompiledExpression(cacheKey, task);
   } else {
     return null;
   }
 }

集合类型如下:

//普通集合为ConcurrentHashMap
private final ConcurrentHashMap<String, FutureTask<Expression>> expressionCache =
    new ConcurrentHashMap<String, FutureTask<Expression>>();
//利用LinkedHashMap实现的LRU缓存集合
private LRUMap<String, FutureTask<Expression>> expressionLRUCache;

public class LRUMap<K, V> extends LinkedHashMap<K, V> {
  static final long serialVersionUID = -1L;

  private final int maxCapacity;

public class LRUMap<K, V> extends LinkedHashMap<K, V> {
	public LRUMap(final int maxCapacity) {
	  super(16, 0.75f, true);
	  if (maxCapacity <= 0) {
	    throw new IllegalArgumentException("Invalid maxCapacity: " + maxCapacity);
	  }
	  this.maxCapacity = maxCapacity;
	}
	@Override
	protected boolean removeEldestEntry(final java.util.Map.Entry<K, V> eldest) {
	  return this.size() > this.maxCapacity;
	}
}

应用场景思考

1.公式计算
2.数据处理及转换
3.数据核对
4.工作流逻辑判定
5.鉴权校验
6.规则引擎

实践案例:

1.工作流逻辑判定

业务需求:
“根据不同的角色和动作决定结果”

表达式设计:
“flow(role,action)”

函数说明:
flow() : 获取角色、动作,执行结果

public class RuleEngineDemo {
    public static void main(String[] args) {
        //注册自定义表达式函数
        AviatorEvaluator.addFunction(new FlowFunction());
        //用户指定规则
        String expression = "flow(role,action)";
        Expression compiledExp = AviatorEvaluator.compile(expression);
        //运行时收到数据
        Map<String, Object> fields = new HashMap<String, Object>();
        fields.put("role", "teacher");
        fields.put("action", "talk");
        compiledExp.execute(fields);
        //"The teacher is talking"
        Map<String, Object> fields2 = new HashMap<String, Object>();
        fields.put("role", "student");
        fields.put("action", "study");
        //"The students are studying"
        compiledExp.execute(fields2);
    }
 
    static class FlowFunction extends AbstractFunction {
        @Override
        public void call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
            String role = FunctionUtils.getStringValue(arg1, env);
            String action = FunctionUtils.getStringValue(arg2, env);
            System.out.println("role : " + role + "action : " + action);
			switch(role){
			   case "teacher": {
			       teacher(action);
			       break;
			   }
			   case "student": {
			       student(action);
			       break;
			   }
			   default: {
			       System.out.println("error role");
			       break;
			   }
			}
        }
        public String getName() {
            return "flow";
        }
    }
}

2.数据处理及转换

demo2转载自:http://shihlei.iteye.com/blog/2421576

业务需求:
“1小时,userid,在ip上,触发action 100次报警”

表达式设计:
“redisCount(‘1’,‘hour’,fields(‘userid,ip,action’)) >= 100”

函数说明:
fields() : 获取字段,校验,生成redis key
redisCount():使用 key进行查询,获取redis中存的量且redis +1

public class RuleEngineDemo {
    public static void main(String[] args) {
        //注册自定义表达式函数
        AviatorEvaluator.addFunction(new FieldsFunction());
        AviatorEvaluator.addFunction(new RedisCountFunction());
        //用户指定规则
        String expression = "redisCount('1','hour',fields('userid,ip,action')) >= 100";
        Expression compiledExp = AviatorEvaluator.compile(expression);
        //运行时收到数据
        Map<String, Object> fields = new HashMap<String, Object>();
        fields.put("userid", "9527");
        fields.put("ip", "127.0.0.1");
        fields.put("phone", "18811223344");
        fields.put("action", "click");
        Boolean needAlarm = (Boolean) compiledExp.execute(fields);
        if (needAlarm) {
            System.out.printf("报警");
        }
    }
 
    static class FieldsFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject fieldsStrObj) {
            //获取可变参数
            String fieldStr = fieldsStrObj.stringValue(env);
            String[] fields = fieldStr.split(",");
            StringBuilder redisKey = new StringBuilder();
            System.out.println("FieldsFunction : " + fieldStr);
            for (String f : fields) {
                Object value = env.get(f);
                if (value != null) {
                    redisKey.append(value.toString());
                } else {
                    //TODO 参数合法性校验
                }
                redisKey.append(":");
            }
            return new AviatorString(redisKey.toString());
        }
        public String getName() {
            return "fields";
        }
    }
 
    static class RedisCountFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
            String period = FunctionUtils.getStringValue(arg1, env);
            String timeUnit = FunctionUtils.getStringValue(arg2, env);
            String redisKey = FunctionUtils.getStringValue(arg3, env);
            System.out.println("FieldsFunction : " + period + " , " + timeUnit + " , " + redisKey);
            //TODO 读取redis
            int redisCount = redisGetAndIncrease(redisKey);
            return new AviatorLong(redisCount);
        }
 
        private int redisGetAndIncrease(String redisKey) {
            System.out.println("get redis : " + redisKey);
            //这里查询redis获得活动的值;
            return 10000;
        }
        
        public String getName() {
            return "redisCount";
        }
    }
}
 类似资料: