aviator(Google Aviator——轻量级 Java 表达式引擎实战)

Google Aviator——轻量级 Java 表达式引擎实战

表达式引擎武艺及比力

Drools简介

Drools(JBoss Rules )是一个开源业务端正引擎,切合业内标准,速率快、听从高。业务分析师或稽核职员可以使用它轻松查察业务端正,从而查验对否已编码的端正实行了所需的业务端正。


除了使用了 Rete 核默算法,开源软件 License 和 100% 的Java完成之外,Drools还提供了很多有效的特性。此中包含完成了JSR94 API和创新的端正语义体系,这个语义体系可用来编写形貌端正的言语。现在,Drools提供了三种语义模块

  • Python模块
  • Java模块
  • Groovy模块


Drools的端正是写在drl文件中。 关于前方的表达式,在Drools的drl文件形貌为:

rule "Testing Comments" when // this is a single line comment eval( true ) // this is a comment in the same line of a pattern then // this is a comment inside a semantic code block end


When表现条件,then是满意条件今后,可以实行的举措,在这里可以调用任何java办法等。在drools不支持字符串的contians办法,只能接纳正则表达式来代替。


IKExpression 简介

IK Expression 是一个开源的、可扩展的, 基于java 言语开发的一个超轻量级的公式化言语剖析实行东西包。IK Expression 不依托于任何第三方的 java 库。它做为一个简便的jar,可以集成于随意的Java 使用中。


关于前方的表达式,IKExpression 的写法为:

public static void main(String[] args) throws Throwable{ E2Say obj = new E2Say(); FunctionLoader.addFunction("indexOf", obj, E2Say.class.getMethod("indexOf", String.class, String.class)); System.out.println(ExpressionEvaluator.evaluate("$indexOf(\"abcd\",\"ab\")==0?1:0")); }

可以看到 IK 是经过自界说函数 $indexOf 来完告捷效的。


Groovy简介

Groovy常常被以为是脚本言语,但是把 Groovy 了解为脚本言语是一种曲解,Groovy 代码被编译成 Java 字节码,然后能集成到 Java 使用步骤中大概 web 使用步骤,整个使用步骤都可以是 Groovy 编写的——Groovy 好坏常机动的。


Groovy 与 Java 平台十分交融,包含多量的java类库也可以直接在groovy中使用。关于前方的表达式,Groovy的写法为:

Binding binding = new Binding(); binding.setVariable("verifyStatus", 1); GroovyShell shell = new GroovyShell(binding); boolean result = (boolean) shell.evaluate("verifyStatus == 1"); Assert.assertTrue(result);


Aviator简介

Aviator是一个高功能、轻量级的java言语完成的表达式求值引擎,主要用于种种表达式的动态求值。如今以前有很多开源可用的java表达式求值引擎,为什么还必要Avaitor呢?


Aviator的计划目标是轻量级和高功能,比拟于Groovy、JRuby的粗笨,Aviator十分小,加上依托包也才450K,不算依托包的话仅有70K;固然,


Aviator的语法是受限的,它不是一门完备的言语,而只是言语的一小局部聚集。


其次,Aviator的完成思绪与其他轻量级的求值器很不相反,其他求值器寻常都是经过表明的办法运转,而Aviator则是直接将表达式编译成Java字节码,交给JVM去实行。简便来说,Aviator的定位是介于Groovy如此的分量级脚本言语和IKExpression如此的轻量级表达式引擎之间。关于前方的表达式,Aviator的写法为:

Map<String, Object> env = Maps.newHashMap(); env.put(STRATEGY_CONTEXT_KEY, context); // triggerExec(t1) && triggerExec(t2) && triggerExec(t3) log.info("### guid: {} logicExpr: [ {} ], strategyData: {}", strategyData.getGuid(), strategyData.getLogicExpr(), JSON.toJSONString(strategyData)); boolean hit = (Boolean) AviatorEvaluator.execute(strategyData.getLogicExpr(), env, true); if (Objects.isNull(strategyData.getGuid())) { //若guid为空,为check告警战略,直接前往 log.info("### strategyData: {} check success", strategyData.getName()); return; }



功能比力


Drools是一个高功能的端正引擎,但是计划的使用场景和在本次测试中的场景并不太一样,Drools的目标是一个繁复目标好比有上百上千的属性,怎样快速婚配端正,而不是简便目标反复婚配端正,因此在这次测试中后果垫底。

IKExpression是依托表明实行来完成表达式的实行,因此功能上去说也差强者意,和Aviator,Groovy编译实行比拟,照旧功能差距照旧分明。


Aviator会把表达式编译成字节码,然后代入变量再实行,全体上功能做得很好。


Groovy是动态言语,依托反射办法动态实行表达式的求值,并且依托JIT编译器,在实行次数够多今后,编译本钱地字节码,因此功能十分的高。对应于eSOC如此必要反复实行的表达式,Groovy是一种十分好的选择。


场景实战

监控告警端正


监控端正设置后果图:


终极转化成表达式言语可以表现为:

// 0.t实体逻辑如下 { "indicatorCode": "test001", "operator": ">=", "threshold": 1.5, "aggFuc": "sum", "interval": 5, "intervalUnit": "minute", ... } // 1.端正掷中表达式 triggerExec(t1) && triggerExec(t2) && (triggerExec(t3) || triggerExec(t4)) // 2.单个 triggerExec 实行内里 indicatorExec(indicatorCode) >= threshold


此时我们只需调用 Aviator 完成表达式实行逻辑如下:

boolean hit = (Boolean) AviatorEvaluator.execute(strategyData.getLogicExpr(), env, true); if (hit) { // 告警 }



自界说函数实战

基于上节监控中央内 triggerExec 函数怎样完成


先看源码:

public class AlertStrategyFunction extends AbstractAlertFunction { public static final String TRIGGER_FUNCTION_NAME = "triggerExec"; @Override public String getName() { return TRIGGER_FUNCTION_NAME; } @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1) { AlertStrategyContext strategyContext = getFromEnv(STRATEGY_CONTEXT_KEY, env, AlertStrategyContext.class); AlertStrategyData strategyData = strategyContext.getStrategyData(); AlertTriggerService triggerService = ApplicationContextHolder.getBean(AlertTriggerService.class); Map<String, AlertTriggerData> triggerDataMap = strategyData.getTriggerDataMap(); AviatorJavaType triggerId = (AviatorJavaType) arg1; if (CollectionUtils.isEmpty(triggerDataMap) || !triggerDataMap.containsKey(triggerId.getName())) { throw new RuntimeException("can't find trigger config"); } Boolean res = triggerService.executor(strategyContext, triggerId.getName()); return AviatorBoolean.valueOf(res); } }


依照官方文档,只需承继 AbstractAlertFunction ,即可完成自界说函数,重点如下:

  • getName() 前往 函数对应的调用称呼,必需完成
  • call() 办法可以重载,尾部参数可选,对应函数入参多个参数分散调用使用


完成自界说函数后,使用前必要注册,源码如下:

AviatorEvaluator.addFunction(new AlertStrategyFunction());

假如在 Spring 项目中使用,只需在 bean 的初始化办法中调用即可。


踩坑指南 & 调优

使用编译缓存形式

默许的编译办法如 compile(script) 、 compileScript(path 以及 execute(script, env) 都不会缓存编译的后果,每次都将重新编译表达式,天生一些匿名类,然后前往编译后果 Expression 实例, execute 办法会持续调用 Expression#execute(env) 实行。


这种形式下有两个成绩:

  1. 每次都重新编译,假如你的脚本没有厘革,这个开支是糜费的,十分影响功能。
  2. 编译每次都产生新的匿名类,这些类会占用 JVM 办法区(Perm 大概 metaspace),内存渐渐占满,并终极触发 full gc。


因此,通常更保举启用编译缓存形式, compile 、 compileScript 以及 execute 办法都有相应的重载办法,允许传入一个 boolean cached 参数,表现对否启用缓存,发起设置为 true:


public final class AviatorEvaluatorInstance { public Expression compile(final String expression, final boolean cached) public Expression compile(final String cacheKey, final String expression, final boolean cached) public Expression compileScript(final String path, final boolean cached) throws IOException public Object execute(final String expression, final Map<String, Object> env, final boolean cached) }


此中的 cacheKey 是用来指定缓存的 key,假如你的脚本特别长,默许使用脚本作为 key 会占用较多的内存并泯灭 CPU 做字符串比力检测,可以使用 MD5 之类唯一的键值来低落缓存开支。


缓存办理

AviatorEvaluatorInstance 有一系列用于办理缓存的办法:

  • 获取如今缓存轻重,缓存的编译后果数目 getExpressionCacheSize()
  • 获取脚本对应的编译缓存后果 getCachedExpression(script) 大概依据 cacheKey 获取 getCachedExpressionByKey(cacheKey) ,假如没有缓存过,前往 null。
  • 没效缓存 invalidateCache(script) 大概 invalidateCacheByKey(cacheKey) 。
  • 清空缓存 clearExpressionCache()


功能发起

  • 优先使用实行优先形式(默许形式)。
  • 使用编译后果缓存形式,复用编译后果,传入不同变量实行。
  • 外部变量传入,优先使用编译后果的 Expression#newEnv(..args) 办法创建外部 env,将会启用标记化,低落变量拜候开支。
  • 消费情况切勿掀开实行跟踪形式。
  • 调用 Java 办法,优先使用自界说函数,其次是导入办法,最初是基于 FunctionMissing 的反射形式。


往期出色

一局部武艺博客:
https://jifuwei.github.io/

群众号:是咕咕鸡

  • 功能调优——小小的log大大的坑
  • 功能优化必备——火焰图
  • Flink 在风控场景及时特性落地实战


参考:

[1].Drools, IKExpression, Aviator和Groovy字符串表达式求值比力

[2].AviatorScript 编程指南

内容底部广告位(手机)
标签:

管理员
草根站长管理员

专注网站优化+网络营销,只做有思想的高价值网站,只提供有担当的营销服务!

上一篇:兰州限行(2023年7月24日今日兰州市限行通知)
下一篇:返回列表