使用aviator表達式進行動態切流

 遇到的問題

最近項目中有這樣一種場景:

需要改變部分訂單的結算方式,這個改動點對交易結算影響很大,需要逐步切流以減少風險。

如果採用case by case硬編碼限定切流的場景來做,就很不靈活,單純這個切流就要上多次線。
因此有這樣的技術需求:使用一種靈活多變的切流方式,即可支持對按照訂單對象任何一個參數滿足某種條件時進行切流,如按照訂單類型字段、某些買家id符合要求。

 

解決方案

經過調研,最終採用aviator表達式來實現(輔以動態內容推送中間件diamond)。

一個簡單的demo便可滿足上述需求。       

NCpsPaymentDTO paymentDTO = newNCpsPaymentDTO();

        paymentDTO.setTkBizTag(5);

        paymentDTO.setTbBuyerId(1234L);

        ExtraInfo extraInfo = new ExtraInfo();

        extraInfo.setEventId(1234567L);

        HashMap<String, Object> paramMap= new HashMap<String, Object>();

        //這裏賦值確保非null,保證配置的表達式可以不用再判空

        paramMap.put("paymentDTO",paymentDTO);

        paramMap.put("extraInfo",extraInfo);



        String configInfo ="paymentDTO.tkBizTag == 5 && paymentDTO.tbBuyerId % 10000 <=2000 && extraInfo.eventId == 1234567";

        Expression expression =AviatorEvaluator.compile(configInfo);

        Boolean rst = (Boolean)expression.execute(paramMap);

        System.out.println(rst); //true

 

Note:

其中configInfo取自動態內容推送中間件diamond。

 

瞭解到這個程度足夠了麼?No.關於aviator還需要知道得更多。

 

Aviator簡介

Aviator是一個高性能、輕量級的java語言實現的表達式求值引擎,主要用於各種表達式的動態求值。現在已經有很多開源可用的java表達式求值引擎,爲什麼還需要Avaitor呢?

 

Aviator的設計目標是輕量級和高性能,相比於Groovy、JRuby的笨重,Aviator非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法是受限的,它不是一門完整的語言,而只是語言的一小部分集合。

 

其次,Aviator的實現思路與其他輕量級的求值器很不相同,其他求值器一般都是通過解釋的方式運行,而Aviator則是直接將表達式編譯成Java字節碼,交給JVM去執行。簡單來說,Aviator的定位是介於Groovy這樣的重量級腳本語言和IKExpression這樣的輕量級表達式引擎之間。

 

Aviator的特性

支持大部分運算操作符,包括算術操作符、關係運算符、邏輯操作符、正則匹配操作符(=~)、三元表達式?:,並且支持操作符的優先級和括號強制優先級,具體請看後面的操作符列表。

支持函數調用和自定義函數

支持正則表達式匹配,類似Ruby、Perl的匹配語法,並且支持類Ruby的$digit指向匹配分組。

自動類型轉換,當執行操作的時候,會自動判斷操作數類型並做相應轉換,無法轉換即拋異常。

支持傳入變量,支持類似a.b.c的嵌套變量訪問。

性能優秀

 

Aviator的限制

沒有if else、do while等語句,沒有賦值語句,僅支持邏輯表達式、算術表達式、三元表達式和正則匹配。

沒有位運算符

 

最新jar包

            <dependency>

               <groupId>com.googlecode.aviator</groupId>

               <artifactId>aviator</artifactId>

               <version>2.3.3</version>

            </dependency>

 

 

Aviator用法

 

算術表達式     

   Long result = (Long)AviatorEvaluator.execute("1+2+3");

        System.out.println(result); //6

note:Aviator的數值類型僅支持Long和Double,任何整數都將轉換成Long,任何浮點數都將轉換爲Double,包括用戶傳入的變量數值。

 

邏輯表達式 

       Boolean result2 = (Boolean)AviatorEvaluator.execute("3>1 && 2!=4 || true");

        System.out.println(result2); //true

 

變量和字符串相加     

  Map<String, Object> env = newHashMap<String, Object>();

        env.put("yourname","aviator");

        String result3 = (String)AviatorEvaluator.execute(" 'hello ' + yourname ", env);

        System.out.println(result3);

上面的例子演示了怎麼向表達式傳入變量值,表達式中的yourname是一個變量,默認爲null,通過傳入Map<String,Object>的變量綁定環境,將yourname設置爲你輸入的名稱。env的key是變量名,value是變量的值。

 

Aviator 2.2開始新增加一個exec方法,可以更方便地傳入變量並執行,而不需要構造env這個map了:
       

 String result4 = (String) AviatorEvaluator.exec(" 'hello ' + yourname ","aviator2");
 System.out.println(result4);

三元表達式

String result5 =(String)AviatorEvaluator.execute("3>0? 'yes':'no'"); 
System.out.println(result5);

 

函數調用

AviatorEvaluator.execute("string.length('hello')");    //求字符串長度
AviatorEvaluator.execute("string.contains('hello','h')");  //判斷字符串是否包含字符串AviatorEvaluator.execute("string.startsWith('hello','h')");  //是否以子串開頭AviatorEvaluator.execute("string.endsWith('hello','llo')"); 是否以子串結尾
AviatorEvaluator.execute("math.pow(-3,2)");   // 求n次方
AviatorEvaluator.execute("math.sqrt(14.0)");   //開 平方根
AviatorEvaluator.execute("math.sin(20)");    //正弦函數

 

還有一些更細緻的用法,詳情可以參考官方文檔:https://github.com/killme2008/aviator/wiki

 參考文章
http://www.blogjava.net/killme2008/archive/2010/06/29/324758.html

http://blog.csdn.net/keda8997110/article/details/50782848 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章