生活随笔
收集整理的這篇文章主要介紹了
流行的表达式引擎简单分析对比
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
序
???????最近來了個能耗監測的需求,也就是對設備上報數據做一些業務的計算后閾值預警風控類的。對需求進行抽絲剝繭的拆解后,發現除去業務,最難的點也就是閾值比較了,到此有經驗的碼農很容易就想到了表達式計算吧。基本的規則增刪改查、邏輯運算表達式生成做完后,就要著手啃最難的骨頭了。考慮到設備上報數據的量,肯定要考慮性能,所以先做表達式的選型。
一、依賴引入
???????這里統一把我這邊準備選用的表達式引擎依賴包都導入,后面就不單個說明,每個引入看引入代碼上面的一行注釋。另外一些老掉牙的引擎,沒有維護的引擎,我這里就是直接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
>
這里說明下:
hutool是因為我要用到它的表達式引擎封裝工具javascript-graalvm版本,本來最新版本都是6.1.2了,這里用的5.12.0是因為我的JDK是8,各位博友如果是高版本的可以用6+,可能獲取引擎的方式不太一樣,可以查apigraalvm 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());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
ScriptEngine最慢,難怪jdk8之后移除了mvel有段時間沒有更新了額,這里是mvel2一行純表達式執行,沒有出現不一致,可見都可靠
四、總結
調用方法MVEL是真飄逸graalvm的Context方式是真快啊(不要懷疑,親測屏蔽所有其他方法,依然是這個0結果,就是光一樣的男人)hutool的封裝的表達式工具ExpressionUtil,要是提供getEngin(“enginName”)就更nice了,現在的自定義引擎獲取要強轉處理異常,不夠優雅graalvm多語言引擎,可以集成ruby,js,python,groovy,kotlin等,總之是很強大,而且有eclipse、oracle加持JEXL表達式語言,標準,靈活,主要是標準,這樣就不會出現執行結果不一致的情況SpelExpressionParser是spring內置的,spring加持aviator高性能、輕量級的 java
語言實現,google加持
???????最后,本次簡單對比就到這里,關于選用、詳情情況,各位博友可自行深挖研究。
我決定選graalvm,原因如下:
畢竟有2大護法加持,雖然發展還不全面(版本迭代快)后期可以開放一個入口,讓會json、js的就可以做一些事情,比如設備指令解析、上報數據異常報警
等等,甚至有些業務校驗都可以開放到前臺去寫js,避免前端、后端對于數據增刪改做業務校驗寫死代碼。
???????下次有驅動場景,再分享更高級的表達式校驗、場景引入等等,希望能幫到大家,Progress together
總結
以上是生活随笔為你收集整理的流行的表达式引擎简单分析对比的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。