java結合luaj實現服務中執行動態腳本

業務場景
咱們都會碰到這樣的狀況,某某用戶臨時又增長新的校驗規則需求,可是需求又很碎很急,無法等到下一個版本上線(想打人有木有),這種時候若是爲了上線該改動須要重啓服務,修改代碼,除了增長開發工做量之外還增大了服務運維風險。咱們固然可使用相似weblogic之類支持熱部署的服務器,可是這對大部分公司顯然不適用。 
若是這個時候可以將代碼邏輯配置到數據庫中,讓運維人員編寫簡單的邏輯便可知足需求而不須要大動干戈豈不是極好?因此這裏我引入了luaj腳本這一律念。java

技術介紹
lua是一種輕量級、支持交互式式編程的腳本語言,在redhat、centos中都有自帶。 
luaj即爲LuaJavaBridge,提供與Java互相嵌入的支持。mysql

交互調用
判斷規則仍然利用lua語言實現,存入數據庫中,java首先從數據庫中讀出判斷腳本而後利用luaj ScriptEngineManager執行腳本,腳本中接受從java傳入的json字符串參數,調用第三方lua解析爲json對象,進行邏輯判斷後返回結果(json字符串)。在luaj中只能使用.lua文件,沒法調用.so的C庫。web

業務場景是根據不一樣的產品類型(測試列子一對一)獲取對應的校驗規則腳本,並執行腳本。sql

1、maven依賴
<!-- luaj -->
<dependency>
     <groupId>org.luaj</groupId>
     <artifactId>luaj-jse</artifactId>
     <version>3.0.1</version>
</dependency>
1
2
3
4
5
6
2、創建規則表
規則表 t_test_ruleshell

CREATE TABLE `t_test_rule` (
  `shell` varchar(5000) NOT NULL COMMENT 'lua腳本',
  `set_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '建立時間',
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `type` int(1) DEFAULT '0' COMMENT '產品類型',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
1
2
3
4
5
6
7
3、編寫校驗腳本
(demo)數據庫

package.path="F:/?.lua" --引入第三方lua,用於處理json,在luaj模式下只能使用.lua文件而沒法使用編譯好的.so庫
local dkjson = require("dkjson")
local str = "{\"code\":0,\"msg\":\"success!\"}"
local retObj,pos,err = dkjson.decode(str , 1, nil)--解析json字符串
if(myParams==nil)
then
  retObj.code=1
  retObj.msg="入參丟失!"
  local retStr = dkjson.encode(retObj)
  return retStr
end編程


local jsonParams,jpos,jerr = dkjson.decode(otaParams , 1, nil)
if(jsonParams==nil)
then
  retObj.code=1
  retObj.msg=err
  local retStr = dkjson.encode(retObj)
  return retStr
endjson

if(jsonParams.version~=nil) then
 local sversion = string.gsub(version,"%p","")--lua字符串過濾,去除.
 local num = tonumber(sversion)
 if(num <= 231) 
  then 
   retObj.code=1 
   retObj.msg="版本低於2.3.1"
 end
endcentos

local retStr = dkjson.encode(retObj)
return retStr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java調用,這裏使用的是jfinal框架服務器

public static boolean checkTheRule(Map<String,String> map, String params) {
        boolean flag = true;
        String type = map.get("type") + "";
        //規則查詢
        List<Record> shells = Db.find("SELECT t.shell from t_test_rule t where type = ?",type);

        if (shells != null) {
            for (Record tmp : shells) {
                String shell = tmp.getStr("shell");
                ScriptEngineManager mgr = new ScriptEngineManager();
                ScriptEngine e = mgr.getEngineByName("luaj");

                e.put("myParams", params);//參數傳入,這裏也能夠傳入java類,成爲在lua中調用執行的方法,好比mysql鏈接封裝方法
                try {
                    String resultMsg = e.eval(shell)+"";//執行並獲取返回結果
                    if(resultMsg==null || resultMsg==""){
                        log.info("lua腳本執行返回空 ");
                        flag = false;
                        break;
                    }
                    RuleResult retObj = JSON.parseObject(resultMsg, RuleResult.class);
                    if(retObj!=null){
                        if(retObj.getCode()==0){
                            flag = true;
                        }else{
                            flag = false;
                            break;
                        }
                        log.info(retObj.getMsg());
                    }else{
                        log.info("json轉換失敗");
                        flag = false;
                        break;
                    }
                } catch (ScriptException e1) {
                    log.info("error:"+e1);
                    flag = false;
                    break;
                }
            }
        } else {
            log.info("沒有找到"+type+"對應的規則!");
            flag = true;
        }
        return flag;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
測試入口類

    @Test
    public void testPrint(){
        initConfig();
        Map map = new Hashtable();
        map.put("type", "38");
        map.put("version", "Ld2017");
        String params =  "{\"version\":\"2.3.0\"}";
        try {
            boolean flag = checkTheRule(map ,params);
            System.out.println(flag);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
4、運行test結果
這裏寫圖片描述

後記
luaj中的lua腳本若是要使用第三方方法,可使用如下兩種方法:


引入第三方lua文件
直接將封裝的Java方法傳入引擎中 
第三方lua引用,用於解析JSON
package.path="xxx/?.lua"
local dkjson = require("dkjson")
1
2
Java方法設置,用於mysql讀取

/** 將java數據庫查詢方法丟入lua中使用 **/
e.put("readTable", new LuaMysqlConnector());
1
2
import java.util.List;

import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.TwoArgFunction;

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;

/**
 * 用於luaj在lua腳本中執行mysql查詢
 * @author zh
 *
 */
public class LuaMysqlConnector extends TwoArgFunction{

    @Override
    /*
     * 入參lstatm 執行sql語句 retString 返回字段字符串;分割
     * @see org.luaj.vm2.lib.TwoArgFunction#call(org.luaj.vm2.LuaValue, org.luaj.vm2.LuaValue)
     */
    public LuaValue call(LuaValue lstatm, LuaValue retString) {
        /** 健壯性校驗 **/
        if (lstatm == null || lstatm.tojstring().equals("")) {
            return LuaValue.valueOf("丟失查詢sql");
        }
        if (retString == null
                || retString.tojstring().equals("")) {
            return LuaValue.valueOf("丟失返回字段列表");
        }

        String[] retLabels = retString.tojstring().split(";");
        StringBuffer sb = new StringBuffer("[");
        List<Record> rets = Db.find(lstatm.tojstring());
        for (int r = 0; r < rets.size(); r++) {
            // 組裝爲json格式
            sb.append("{");
            for (int i = 0; i < retLabels.length; i++) {
                sb.append("\"");
                sb.append(retLabels[i]);
                sb.append("\":\"");
                sb.append(rets.get(r).getStr(retLabels[i]));
                sb.append("\"");
                if (i != (retLabels.length - 1)) {
                    sb.append(",");
                }
            }
            if (r == (rets.size() - 1)) {
                sb.append("}");
            } else {
                sb.append("},");
            }

        }
        sb.append("]");
        return LuaValue.valueOf(sb.toString());
    }

}

相關文章
相關標籤/搜索