最近来了个能耗监测的需求,也就是对设备上报数据做一些业务的计算后阈值预警风控类的。对需求进行抽丝剥茧的拆解后,发现除去业务,最难的点也就是阈值比较了,到此有经验的码农很容易就想到了表达式计算吧。基本的规则增删改查、逻辑运算表达式生成做完后,就要着手啃最难的骨头了。考虑到设备上报数据的量,肯定要考虑性能,所以先做表达式的选型。

一、依赖引入

这里统一把我这边准备选用的表达式引擎依赖包都导入,后面就不单个说明,每个引入看引入代码上面的一行注释。另外一些老掉牙的引擎,没有维护的引擎,我这里就是直接pass了,要看引擎的活力,在maven找它的jar时看更新时间、周期。
这里再插一句CSDN浏览器插件真香,就说在maven中心库找jar吧。CSDN浏览器插件提供的shift + c调出插件,输入mvn,立刻就可以输入jar的名称或骨架名称,回车,jar就出来了,上个效果图吧。

 <!-- 好用的工具类合集 -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.13</version>
</dependency>
<!-- aviator表达式引擎支持 -->
<dependency><groupId>com.googlecode.aviator</groupId><artifactId>aviator</artifactId><version>5.2.7</version>
</dependency><!-- jexl3表达式引擎支持 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-jexl3</artifactId><version>3.2.1</version>
</dependency><!-- javascript-graalvm支持 -->
<dependency><groupId>org.eclipse.dirigible</groupId><artifactId>dirigible-engine-javascript-graalvm</artifactId><version>5.12.0</version>
</dependency><!-- graalvm js支持 -->
<!-- <dependency><groupId>org.graalvm.js</groupId><artifactId>js</artifactId><version>21.3.0</version>
</dependency>--><!-- mvel支持 -->
<dependency><groupId>org.mvel</groupId><artifactId>mvel2</artifactId><version>2.4.13.Final</version>
</dependency>

这里说明下:

  1. hutool是因为我要用到它的表达式引擎封装工具
  2. javascript-graalvm版本,本来最新版本都是6.1.2了,这里用的5.12.0是因为我的JDK是8,各位博友如果是高版本的可以用6+,可能获取引擎的方式不太一样,可以查api
  3. graalvm js注释了,是因为javascript-graalvm里依赖了它,可以点进去看

二、性能测试demo

这里今天是做纯表达式的性能测试,因为目前我这边需求只需要一行逻辑运算表达式做阈值校验就ok了。实际上有的引擎功能非常强大,可以直接就支持部分语言的整个方法执行的。比如graalvm,这个是oracle现在强烈推荐的,非常牛逼,最后总结的时候,我说对于它的想法。
测试demo就是对表达式做值替换后的逻辑运算符的布尔计算结果,表达式如下:
((dayUse > 3 && dayUse < 7 ) || (aloneUse > 100 || aloneUse < 0 )) && (totalUse > 1000 )

@Test
void checkExpre() throws Exception {log.info("--表达式校验测试--");//通用条件、参数处理Object eval = null;String orginExpre = "((dayUse > 3 && dayUse < 7 ) || (aloneUse > 100 || aloneUse < 0 )) && (totalUse > 1000 ) ";AtomicReference<String> expre = new AtomicReference<>(orginExpre);Dict dict = Dict.create().set("dayUse", 4).set("aloneUse", 3).set("totalUse", 1);dict.entrySet().stream().forEach(t -> {String tmpExpre = expre.get().replace(t.getKey(), t.getValue().toString());expre.set(tmpExpre);});log.info("-----纯表达式性能测试开始----");StopWatch sw = new StopWatch();sw.start();eval = ExpressionUtil.eval(expre.get(), dict);sw.stop();log.info("--aviator--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());//获取js引擎实例sw = new StopWatch();sw.start();ScriptEngineManager sem = new ScriptEngineManager();ScriptEngine engine = sem.getEngineByName("javascript");eval = engine.eval(expre.get());sw.stop();log.info("--ScriptEngine--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();engine = new JexlScriptEngine();eval = engine.eval(expre.get());sw.stop();log.info("--Jexl3--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();ExpressionParser p = new SpelExpressionParser();Expression exp = p.parseExpression(expre.get());eval = exp.getValue();sw.stop();log.info("--spel--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();GraalJSEngineFactory graalJSEngineFactory = new GraalJSEngineFactory();GraalJSScriptEngine graalJSScriptEngine = graalJSEngineFactory.getScriptEngine();eval = graalJSScriptEngine.eval(expre.get());sw.stop();log.info("--graalvm--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();Context context = Context.newBuilder().allowAllAccess(true).build();eval = context.eval("js", expre.get());log.info("--graalvm js 方式1--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();ScriptEngine eng = new ScriptEngineManager().getEngineByName("js");eval = eng.eval(expre.get());sw.stop();log.info("--graalvm js 方式2--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());sw = new StopWatch();sw.start();eval = MVEL.eval(orginExpre, dict);sw.stop();log.info("--MVEL--expre:{} = {},耗时:{}ms", expre.get(), eval, sw.getTotalTimeMillis());}

三、demo执行结果


很明显graalvm表达式引擎确实一骑绝尘。
纯表达式(这里要说严谨)执行性能结论:
graalvm < Jexl3 < MVEL2 < spel < aviator < ScriptEngine

  1. ScriptEngine最慢,难怪jdk8之后移除了
  2. mvel有段时间没有更新了额,这里是mvel2
  3. 一行纯表达式执行,没有出现不一致,可见都可靠

四、总结

  1. 调用方法MVEL是真飘逸
  2. graalvm的Context方式是真快啊(不要怀疑,亲测屏蔽所有其他方法,依然是这个0结果,就是光一样的男人)
  3. hutool的封装的表达式工具ExpressionUtil,要是提供getEngin(“enginName”)就更nice了,现在的自定义引擎获取要强转处理异常,不够优雅
  4. graalvm多语言引擎,可以集成ruby,js,python,groovy,kotlin等,总之是很强大,而且有eclipse、oracle加持
  5. JEXL表达式语言,标准,灵活,主要是标准,这样就不会出现执行结果不一致的情况
  6. SpelExpressionParser是spring内置的,spring加持
  7. aviator高性能、轻量级的 java
    语言实现,google加持

最后,本次简单对比就到这里,关于选用、详情情况,各位博友可自行深挖研究。
我决定选graalvm,原因如下:

  1. 毕竟有2大护法加持,虽然发展还不全面(版本迭代快)
  2. 后期可以开放一个入口,让会json、js的就可以做一些事情,比如设备指令解析、上报数据异常报警
    等等,甚至有些业务校验都可以开放到前台去写js,避免前端、后端对于数据增删改做业务校验写死代码。

下次有驱动场景,再分享更高级的表达式校验、场景引入等等,希望能帮到大家,Progress together

流行的表达式引擎简单分析对比相关推荐

  1. antlr表达式引擎

    初次在项目中使用antlr,刚做了第一版,功能很简单(参不多正则都能做╮(╯▽╰)╭) 用antlr做表达式引擎,分析表达式变量和函数 举例:表达式为[a]+[b]>[c]*([d]+[e])  ...

  2. java fel_Java表达式引擎fel/groovy/expression4j/java脚本引擎的性能对比【原创】

    又是性能对比,最近跟性能较上劲了. 产品中需要用到数学表达式,表达式不复杂,但是对性能要求比较高.选用了一些常用的表达式引擎计算方案,包含:java脚本引擎(javax/script).groovy脚 ...

  3. 最简单却又极具扩展性的Java表达式引擎,自创编程语言必备

    这个表达式引擎只有短短的100多行,却能实现包括加减乘除.括号优先级在内的运算,可以在"处理表达式"函数中自行扩展想要处理的算法.这里面算法的难点主要在于如何实现在多级括号存在的情 ...

  4. 简单分析几十个游戏案例

    文章目录 一. 介绍 二. 影响游戏体验的因素 三. 游戏能爆火的因素 1.影响游戏爆火因素的排名 2.玩游戏的两种经典心理 3.经典案例分析 Qq农场 植物大战僵尸 水果忍者 召唤神龙 羊了个羊 4 ...

  5. 表达式引擎在转转平台的实践

    一.业务背景介绍 笔者负责了转转APP后端研发工作,主要的模块有首页.列表.详情页.个人中心等.在负责的详情页模块中,有这样的一个场景,APP打开不同商品的时候,会根据商品所属的业务,跳转到对应业务所 ...

  6. 几十款游戏的简单分析

    文章目录 一. 介绍 二. 影响游戏体验的因素 三. 游戏能爆火的因素 1.影响游戏爆火因素的排名 2.玩游戏的两种经典心理 3.经典案例分析 Qq农场 植物大战僵尸 水果忍者 召唤神龙 羊了个羊 4 ...

  7. 表达式引擎Mvel详解与实战

    前言 表达式引擎是规则引擎的一部分,是一种嵌入在应用程序中的组件,实现了逻辑判断与程序代码中分离出来,并使用预定义的语义脚本编写业务条件.接受数据输入,解释执行表达式,并返回表达式执行结果. 在前面的 ...

  8. 信息抽取(五)实体命名识别之嵌套实体识别哪家强,我做了一个简单的对比实验

    实体命名识别之嵌套实体识别哪家强 嵌套实体识别 实体矩阵构建框架 方法比较 GlobalPointer TPLinker Tencent Muti-head Deep Biaffine 实验结果 总结 ...

  9. CVer最想知道的,简单分析下《2020年度中国计算机视觉人才调研报告》

    文章首发于CVer最想知道的,简单分析下<2020年度中国计算机视觉人才调研报告> 最近闲来无事,老潘以一名普通算法工程师的角度,结合自身以及周围人的情况,理性也感性地分析一下极市平台前些 ...

最新文章

  1. 背景建模--Vibe 算法优缺点分析
  2. [leetcode] 22. Generate Parentheses(medium)
  3. ConcurrentHashMap 源码分析
  4. php 将字符串打乱,PHP内部实现打乱字符串顺序函数str_shuffle的方法
  5. cisco3550交换机限速配置
  6. 多线程-ThreadLocal,InheritableThreadLocal
  7. 异常之【You have an error in your SQL syntax】
  8. domain name
  9. WEB前端性能优化常见方法
  10. 计算机安装pdf打印机驱动,Win7安装Microsoft Print to PDF虚拟打印机的方法
  11. 计算机自动生成凭证,哪些财务软件能自动生成记账凭证?
  12. JavaScript(6):回调函数
  13. 《拆掉思维里的墙》读书笔记
  14. 华为官方翻新产品秒杀活动来袭,官方正品,7折优惠,真香!
  15. 阿里云物联网Iot设备上下线状态数据流转的设置
  16. IT项目经理在面试时如何巧妙的回答老板提出的问题
  17. ACP学习笔记(云计算)附题
  18. 使用htmlunit采集网页+点击网页按钮
  19. 在Arcgis地图上绘制Echarts热力图(Heatmap)
  20. D3D12渲染技术之混合

热门文章

  1. 智慧树知识图谱笔记1.1
  2. 运动场围栏、校园赏析、户外广告、校园广告、格言传播
  3. ORA00942,ORA06550报错解决
  4. 北京个人ADSL和企业ADSL有什么区别啊?
  5. 云大计算机复试 贴吧,云大19新传复试经验贴
  6. 转载 前端基础知识体系 一个新手的学习之路
  7. IT从业者成过劳死高危人群 员工不满病态企业文化
  8. hive配置MySQL初始化问题
  9. shell-awk的BEGIN和END
  10. iOS--如何创建pod库