業務場景
咱們都會碰到這樣的狀況,某某用戶臨時又增長新的校驗規則需求,可是需求又很碎很急,無法等到下一個版本上線(想打人有木有),這種時候若是爲了上線該改動須要重啓服務,修改代碼,除了增長開發工做量之外還增大了服務運維風險。咱們固然可使用相似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());
}
}