1. 程式人生 > >java結合luaj實現服務中執行動態指令碼

java結合luaj實現服務中執行動態指令碼

業務場景

我們都會碰到這樣的情況,某某使用者臨時又增加新的校驗規則需求,但是需求又很碎很急,沒法等到下一個版本上線(想打人有木有),這種時候如果為了上線該改動需要重啟服務,修改程式碼,除了增加開發工作量以外還增大了服務運維風險。我們當然可以使用類似weblogic之類支援熱部署的伺服器,但是這對大部分公司顯然不適用。
如果這個時候能夠將程式碼邏輯配置到資料庫中,讓運維人員編寫簡單的邏輯即可滿足需求而不需要大動干戈豈不是極好?所以這裡我引入了luaj指令碼這一概念。

技術介紹

lua是一種輕量級、支援互動式式程式設計的指令碼語言,在redhat、centos中都有自帶。
luaj即為LuaJavaBridge,提供與Java互相嵌入的支援。

互動呼叫

判斷規則仍然利用lua語言實現,存入資料庫中,java首先從資料庫中讀出判斷指令碼然後利用luaj ScriptEngineManager執行指令碼,指令碼中接受從java傳入的json字串引數,呼叫第三方lua解析為json物件,進行邏輯判斷後返回結果(json字串)。在luaj中只能使用.lua檔案,無法呼叫.so的C庫。

業務場景是根據不同的產品型別(測試列子一對一)獲取對應的校驗規則指令碼,並執行指令碼。

一、maven依賴

<!-- luaj -->
<dependency>
     <groupId>org.luaj</groupId
>
<artifactId>luaj-jse</artifactId> <version>3.0.1</version> </dependency>

二、建立規則表

規則表 t_test_rule

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;

三、編寫校驗指令碼

(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
end

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
end

local retStr = dkjson.encode(retObj)
return retStr

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;
    }

測試入口類

    @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();
        }

    }

四、執行test結果

這裡寫圖片描述

後記

luaj中的lua指令碼如果要使用第三方方法,可以使用以下兩種方法:


  • 引入第三方lua檔案
  • 直接將封裝的Java方法傳入引擎中
第三方lua引用,用於解析JSON
package.path="xxx/?.lua"
local dkjson = require("dkjson")

Java方法設定,用於mysql讀取

/** 將java資料庫查詢方法丟入lua中使用 **/
e.put("readTable", new LuaMysqlConnector());
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());
    }

}

相關推薦

java結合luaj實現服務執行動態指令碼

業務場景 我們都會碰到這樣的情況,某某使用者臨時又增加新的校驗規則需求,但是需求又很碎很急,沒法等到下一個版本上線(想打人有木有),這種時候如果為了上線該改動需要重啟服務,修改程式碼,除了增加開發工作量以外還增大了服務運維風險。我們當然可以使用類似weblog

存儲過程執行動態Sql語句

查詢 需要 一點 定義 data 復制 ast 我們 臨時   在最近的項目中,有用到動態執行sql語句,SQL為我們提供了兩種動態執行SQL語句的命令,分別是EXEC和sp_executesql;通常,sp_executesql則更具有優勢,它提供了輸入輸出接口,而EXE

怎樣SQL存儲過程執行動態SQL語句

緩存 參數 insert 每次 一點 證明 record scac 清空緩存 MSSQL為我們提供了兩種動態執行SQL語句的命令,分別是EXEC和sp_executesql;通常,sp_executesql則更具有優勢,它提供了輸入輸出接口,而EXEC沒有。還有一個最大的好

關於Ocelot 網關結合Consul實現服務轉發的坑爹問題

但是 如果 template streams 驗證 ports style conf 結合 下面是我的網關配置來驗證下Ocelot的問題,如果只是做網關轉發應該還ok,但是要是結合Consul來檢查並健康的轉發有效服務器還是有很多弊端 下面用代碼來說明下: {

DB2 存儲過程執行動態SQL

values exec 退出 .... strong arch 包含 gin 使用 樣本代碼: DROP PROCEDURE QUOTATION.COPY_SAMPLE; CREATE PROCEDURE QUOTATION.COPY_SAMPLE ( IN tab

純前端表格控制元件SpreadJS與Java結合實現模板上傳和下載等功能

安裝部署: 開發環境: 後臺伺服器端採用 Java 、SpringMVC。 資料儲存到 \demo_web\WEB-INF\FileDatabase.txt 檔案中,不需要依賴DB。 採用 Eclipse Java EE IDE for Web Develope

java.TCP通訊(實現服務端與客戶端簡單的聊天)

要求: 編寫兩個java application應用程式,完成以下功能: 一個程式為服務端,建立TCP服務端套接字。 l另外一個程式為客戶端,建立TCP客戶端套接字。 這兩個程式可以互聯,完成一個基於TCP/IP網路的文字聊天程式。 java原始碼如下: 1)基

將maven建立的java工程部署到jetty執行

1.法1: 在pom中配置 <build>         <finalName>testdemo</finalName>         <plugins>             <plugin>       

Java執行Groovy指令碼

        最近專案有個需求,需要執行一個方法,方法的目的是輸入一個金額XXX,根據抹零方式算出抹零後的金額,但是有個問題,困難場景:上游又獲取不到抹零的方式;下游又獲取不到XXX這個金額;這個時候需要寫一個Groovy指令碼,他有一個入參變數XXX,為抹零前的金額,我

Java執行JS指令碼

Java是靜態語言,需要經過預先編譯,所以在一些需求變動比較頻繁的場景,使用起來很不方便,這個時候如果我們引入指令碼來實現變動頻繁的需求,就會很爽了。 本文為在網友的文章基礎上整理,記錄在Java中執行JavaScript的方法,以此備忘。 我們要執行的JavaScript指令碼co

【開發筆記】Java和.Net實現JS的escape()和unescape()

在做建行支付對接時,傳的引數中的漢字,需要用escape()編碼 但我是通過Java和.Net後臺直接請求,因此需要在後臺實現類似的功能 所以在此貼上程式碼 Java /** * 漢字編碼,實現js escape() * * @p

sqlserver在觸發器執行動態sql用到inserted(deleted)表

  這兩天公司產品中要加個資料同步用的觸發器,oracle版沒什麼問題,很順利的寫完了。       在寫sqlserver版時遇到了麻煩。由於在觸發器中要根據inserted(deleted)表的資料,對另一個表進行更新操作,而這個表的某些欄位是不固定的,所以需要寫成動態

java結合redis實現分散式鎖

今天工作之餘,檢視一下利用redis來實現分散式鎖,因此,在檢視別人文章之餘,自己也來手動模擬實現Java的lock介面,來自己手動實現一個分散式鎖。擁有簡單的加鎖,解鎖,鎖中斷等操作。 利用redis的分散式鎖,主要還是利用redis的setnx命令,檢視redis文件,

儲存過程執行動態Sql語句

  MSSQL為我們提供了兩種動態執行SQL語句的命令,分別是EXEC和sp_executesql;通常,sp_executesql則更具有優勢,它提供了輸入輸出介面,而EXEC沒有。還有一個最大的好處就是利用sp_executesql,能夠重用執行計劃,這就大大提供了執

WEBLOGIC 10.3 在Windows後臺服務執行

weblogic10.3 在windows後臺服務中執行配置方式與weblogic 8 已經存在很大的區別。 weblogic 8 : 1: 在D:/bea/user_projects/domains/mydomain目錄下的installService.cmd檔案中的:

Java執行SQL指令碼總結

利用 Ant 的SQL Task來實現執行SQL 指令碼的功能。 ant 包中的 SQLExec類的擴充套件,此時需要將ant 包(ant.jar)匯入 SQLExec sqlExec = new S

Oracle 儲存過程執行動態SQL,動態表名

create or replace procedure mw_sys.clearrubbishdatefy2 is type table_type is table of mw_app.mwt_ud_yscsjdl.tablename%type; tablenameAr

在pl/sql執行動態sql

       動態sql就是把sql寫在一個字串裡,在儲存過程中解析字串執行sql。這種動態sql很多時候會在別的語言裡寫,再連線資料庫進行操作,這樣的確方便很多,例如在java中使用JDBC。但是如

說說如何在 Java 執行 JavaScript 指令碼

Java 是強型別的靜態語言,即所有的程式碼在執行之前都必須先進行嚴格的型別檢查並編譯為 JVM 位元組碼。這樣做雖然保證了安全與效能,但卻犧牲了靈活性。而那些動態語言(JavaScript 、Python 等)卻可以在程式執行時改變程式的結構以及變數型別,因此

java如何來實現MySQLBlob欄位的存取

* Title: BlobPros.java * Project: test * Description: 把圖片存入mysql中的blob欄位,並取出 * Call Module: mtools資料庫中的tmp表 * File: C:downloadsluozsh.jpg * Copyright