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

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

  • Aviator
    • 规则表达式引擎分类
    • Aviator原理及特点
    • Aviator解析
    • 性能执行配置推荐
    • 最佳实践
    • 源码跟踪
      • 编译:
      • 执行:
      • 读取缓存:
    • 应用场景思考
    • 实践案例:
      • 1.工作流逻辑判定
      • 2.数据处理及转换

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.0System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0}}class AddFunction extends AbstractFunction {@Overridepublic 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>() {@Overridepublic 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;//aprivate final AviatorJavaType f1;//bprivate final AviatorJavaType f2;//cpublic 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 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+789return 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;}@Overrideprotected 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 读取redisint 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";}}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部