>  기사  >  데이터 베이스  >  Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

WBOY
WBOY앞으로
2023-06-02 22:41:081380검색

1. LUA 설치

Mac에 LUA를 설치하는 것은 매우 간단합니다. brew 관련 명령을 사용하면 됩니다. brew相关命令;

brew install lua

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

使用lua -v命令可以看到lua已经安装完毕。

1)简单使用

创建一个test.lua文件,内容为:

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

执行命令:

lua test.lua

输出为:

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

二、lua语法简介

Lua 提供了交互式编程和脚本式编程:

  • 交互式编程:直接在命令行中输入语法,可以立即执行并查看到执行效果。

  • 脚本是编程:编写脚本文件,然后再执行。

1、注释

lua提供两种注释方式:单行注释和多行注释

1)单行注释

使用两个减号;

--

2)多行注释
--[[
 多行注释
 多行注释
 --]]

2、关键字

下列为 Lua 的保留关键字,和Java一样 保留关键字不能作为常量或变量。

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

3、变量

默认的情况下,定义一个变量都是全局变量;如果要用局部变量 需要声明为local

1)全局变量

全局变量不需要声明,给一个变量赋值后便创建了这个全局变量;

访问一个没有初始化的全局变量也不会出错,只不过会得到结果:nil

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

只要将变量赋值为nil,就可以删除全局变量;换句话说,当且仅当变量为nil时,该变量不存在。

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

此外,一般以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。

2)局部变量
-- 局部变量赋值
local b=2

4、数据类型

Lua 是一个动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

1)Lua数组

在Lua 数组中,索引值是从 1 开始,可以指定为从 0 开始。

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

2)字符串操作
  • .. 连接两个字符串;

  • string.sub() 用于截取字符串;

string.sub(s, i [, j])
  • s:要截取的字符串;

  • i:截取开始位置;

  • j:截取结束位置,默认为 -1,最后一个字符;

string.find() 用于字符串查找

string.find (str, substr, [init, [plain]])
  • 在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。

  • init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。

  • plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用正则模式匹配。

5、if-else

条件表达式结果可以是任何值,Lua认为false和nil为假,true和非nil为真。

整体的if-else结构和我们使用的高级语言(Java、GO)类似,区别在于:LUA中的if()表达式满足之后想要做一些其余逻辑,需要紧跟then,并且流程控制以end

if(xxx) then
    print("xxx")
else if(xx) then
    print("xx")
else
   print("x")
end

Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

lua -v 명령을 사용하여 lua가 변경되었는지 확인하세요. 설치되었습니다.
1) 간단히

를 사용하여 다음 내용이 포함된 test.lua 파일을 만듭니다.

Java 생태학/Redis에서 Lua 스크립트를 사용하는 방법

실행 명령:

for var=exp1,exp2,exp3 do  
    <执行体>  
end

출력은 다음과 같습니다: Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

🎜Java 생태학/Redis에서 Lua 스크립트를 사용하는 방법🎜🎜2. Lua 구문 소개🎜🎜Lua는 대화형 프로그래밍과 스크립트 프로그래밍을 제공합니다.🎜
  • 🎜대화형 프로그래밍: 명령줄에 구문을 직접 입력하면 즉시 실행하여 실행 효과를 확인할 수 있습니다. 🎜
  • 🎜스크립팅은 프로그래밍입니다. 스크립트 파일을 작성한 다음 실행합니다. 🎜
  • 🎜1. 댓글🎜🎜lua는 한 줄 주석과 여러 줄 주석이라는 두 가지 주석 방법을 제공합니다.🎜🎜1) 한 줄 주석🎜🎜두 개의 빼기 기호를 사용하세요.🎜🎜 --🎜🎜2) 여러 줄 주석🎜<pre class="brush:java;">--打印数组a的所有值 a = {&quot;one&quot;, &quot;two&quot;, &quot;three&quot;} for i, v in ipairs(a) do print(i, v) end</pre>🎜2. 키워드🎜🎜다음은 Lua의 예약어입니다. 예약어는 Java와 마찬가지로 상수나 변수로 사용할 수 없습니다. 🎜🎜<img src="https://img.php.cn/upload/article/000/887/227/168571687129252.png" alt="Java 생태학/Redis에서 Lua 스크립트를 사용하는 방법">🎜🎜3 . 변수 🎜🎜기본적으로 변수 정의는 전역 변수이며, 지역 변수를 사용하려면 <code>local로 선언해야 합니다. 🎜🎜1) 전역 변수는 🎜🎜전역 변수입니다. 선언할 필요는 없으며 변수를 제공합니다. 이 전역 변수는 할당 후에 생성됩니다. 🎜🎜초기화되지 않은 전역 변수에 액세스할 때 오류는 발생하지 않지만 결과는 다음과 같습니다: nil🎜🎜Java Ecology/Redis에서 Lua 스크립트를 사용하는 방법🎜🎜변수가 nil에 할당되어 있는 한 전역 변수는 삭제될 수 있습니다. 즉, 변수가 nil인 경우에만 해당 변수가 존재하지 않습니다. 🎜🎜Java 생태학/Redis에서 Lua 스크립트를 사용하는 방법🎜🎜🎜 게다가 일반적으로 밑줄로 시작하고 그 뒤에 대문자 문자열(예: _VERSION)이 오는 이름은 Lua 내부 전역 변수용으로 예약되어 있습니다. 🎜🎜🎜2) 지역 변수🎜
    while(condition)
    do
       statements
    end
    🎜4. 데이터 유형🎜🎜Lua는 동적 유형 언어입니다. 변수는 유형을 지정할 필요가 없으며 변수에만 값을 할당하면 됩니다. 값은 변수에 저장되거나 인수로 전달되거나 결과로 반환될 수 있습니다. 🎜🎜Lua에는 nil, boolean, number, string, userdata, function, thread 및 table의 8가지 기본 유형이 있습니다. 🎜🎜Java 생태학/Redis에서 Lua 스크립트를 사용하는 방법🎜🎜1 ) Lua 배열🎜🎜Lua 배열에서는 인덱스 값이 1부터 시작하며 0부터 시작하도록 지정할 수 있습니다. 🎜🎜Java Ecology/Redis에서 Lua 스크립트를 사용하는 방법🎜🎜2 ) 문자열 작업 🎜
    • 🎜.. 두 문자열 연결 🎜
    • 🎜string.sub () 는 문자열을 가로채는 데 사용됩니다. 🎜
    --[[ 函数返回两个值的最大值 --]]
    function max(num1, num2)
       if (num1 > num2) then
          result = num1;
       else
          result = num2;
       end
       return result; 
    end
    -- 调用函数
    print("两值比较最大值为 ",max(10,4))
    print("两值比较最大值为 ",max(5,6))
    • 🎜s: 가로채는 문자열 🎜
    • 🎜i: 차단 시작 위치; 🎜
    • 🎜j: 차단 끝 위치, 기본값은 -1, 마지막 문자 🎜
    🎜string. )는 문자열 검색에 사용됩니다🎜
    <dependency>
        <groupId>org.luaj</groupId>
        <artifactId>luaj-jse</artifactId>
        <version>3.0.1</version>
    </dependency>
    • 🎜지정된 대상 문자열 str에서 지정된 내용 substr을 검색합니다. 일치하는 부분 문자열이 발견되면 시작 부분 하위 문자열의 인덱스와 끝 인덱스가 반환됩니다. 존재하지 않으면 nil이 반환됩니다. 🎜
    • 🎜init는 검색 시작 위치를 지정합니다. 기본값은 1입니다. 음수일 수 있으며 뒤에서 앞으로 세는 문자 수를 나타냅니다. 🎜
    • 🎜plain은 단순 모드를 사용할지 여부를 나타내며, 기본값은 false이고, true는 하위 문자열에 대한 단순 검색만 ​​수행하고, false는 일반 패턴 일치를 사용함을 나타냅니다. 🎜
    🎜5. if-else🎜🎜 조건식의 결과는 어떤 값이든 가능합니다. Lua는 false와 nil을 false로 간주하고 true와 non-nil을 true로 간주합니다. 🎜🎜전체 if-else 구조는 우리가 사용하는 고급 언어(Java, GO)와 유사합니다. 차이점은 LUA의 if() 표현식이 만족된 후에 다른 논리를 수행하려는 경우 then을 따라야 하며 흐름 제어는 end로 끝납니다. 🎜
    package com.saint.base.lua;
    
    import org.luaj.vm2.Globals;
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.lib.jse.JsePlatform;
    
    public class LuaString {
        public static void main(String[] args) {
            String luaStr = "print &#39;Saint is best man&#39;";
            Globals globals = JsePlatform.standardGlobals();
            LuaValue luaValue = globals.load(luaStr);
            luaValue.call();
        }
    }
    🎜6. 루프🎜🎜1) for 루프🎜🎜Lua 프로그래밍 언어에는 두 가지 주요 범주가 있습니다: 배열 for 루프와 일반 for 루프🎜🎜1> 배열 for 루프🎜🎜🎜문법 형식: 🎜🎜
    package com.saint.base.lua;
    
    import org.luaj.vm2.Globals;
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.lib.jse.JsePlatform;
    
    import java.io.FileNotFoundException;
    
    public class LuaFile {
        public static void main(String[] args) throws FileNotFoundException {
            // lua脚本的文件路径
            String luaPath = "/xxxx/javaTest.lua";
            Globals globals = JsePlatform.standardGlobals();
            //加载脚本文件login.lua,并编译
            globals.loadfile(luaPath).call();
            LuaValue func1 = globals.get(LuaValue.valueOf("print1"));
            func1.call();
    
            LuaValue func2 = globals.get(LuaValue.valueOf("print2"));
            String luaResp = func2.call(LuaValue.valueOf("saint-input-param")).toString();
            System.out.println("lua file return is : " + luaResp);
        }
    }
    🎜 var는 exp1에서 exp2로 변경됩니다. 각 변경은 단계 크기로 exp3을 사용하여 var를 증가시키고 "실행 본문"을 한 번 실행합니다. exp3은 선택 사항이며 지정하지 않은 경우 기본값은 1입니다. 🎜🎜🎜🎜

    2> 泛型for循环

    通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句;

    语法格式:

    --打印数组a的所有值  
    a = {"one", "two", "three"}
    for i, v in ipairs(a) do
        print(i, v)
    end
    • i 是数组索引值,v 是对应索引的数组元素值。

    • ipairs是Lua提供的一个迭代器函数,用来迭代数组。

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    2)while循环

    while 循环语句在判断条件为 true 时会重复执行循环体语句。

    语法格式:

    while(condition)
    do
       statements
    end
    • statements(循环体语句) 可以是一条或多条语句,condition(条件) 可以是任意表达式;

    • 在 condition(条件) 为 true 时执行循环体语句。

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    3)break提前退出循环

    和Java中的break一个作用,用于退出当前循环或语句;

    7、函数

    在Lua中,函数是对语句和表达式进行抽象的主要方法。类似于Java中的方法。

    Lua 函数主要有两种用途:

    • 完成指定的任务,这种情况下函数作为调用语句使用;

    • 计算并返回值,这种情况下函数作为赋值语句的表达式使用;

    函数的编写方式如下:

    --[[ 函数返回两个值的最大值 --]]
    function max(num1, num2)
       if (num1 > num2) then
          result = num1;
       else
          result = num2;
       end
       return result; 
    end
    -- 调用函数
    print("两值比较最大值为 ",max(10,4))
    print("两值比较最大值为 ",max(5,6))

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    三、Java中执行Lua脚本

    Java中执行Lua脚本有两种方式:字符串的方式、文件的方式;

    Java中想要执行LUA脚本,首先需要在pom中引入相关依赖:

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

    1、字符串方式

    对于简单的lua脚本,可以直接用java字符串写;

    package com.saint.base.lua;
    
    import org.luaj.vm2.Globals;
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.lib.jse.JsePlatform;
    
    public class LuaString {
        public static void main(String[] args) {
            String luaStr = "print &#39;Saint is best man&#39;";
            Globals globals = JsePlatform.standardGlobals();
            LuaValue luaValue = globals.load(luaStr);
            luaValue.call();
        }
    }

    控制台输出:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    2、文件方式

    对于一些比较常用的、复杂的脚本可以选择存放在文件中,在Java中再调用lua文件;

    package com.saint.base.lua;
    
    import org.luaj.vm2.Globals;
    import org.luaj.vm2.LuaValue;
    import org.luaj.vm2.lib.jse.JsePlatform;
    
    import java.io.FileNotFoundException;
    
    public class LuaFile {
        public static void main(String[] args) throws FileNotFoundException {
            // lua脚本的文件路径
            String luaPath = "/xxxx/javaTest.lua";
            Globals globals = JsePlatform.standardGlobals();
            //加载脚本文件login.lua,并编译
            globals.loadfile(luaPath).call();
            LuaValue func1 = globals.get(LuaValue.valueOf("print1"));
            func1.call();
    
            LuaValue func2 = globals.get(LuaValue.valueOf("print2"));
            String luaResp = func2.call(LuaValue.valueOf("saint-input-param")).toString();
            System.out.println("lua file return is : " + luaResp);
        }
    }

    lua脚本文件:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    控制台输出:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    3、Luaj概述

    Luaj在包装执行具体的Lua代码时, 有三种不同的模式;

    • 纯脚本解析执行(不选用任何Compiler)

    • To Lua字节码(LuaC, lua-to-lua-bytecode compiler)(默认选用)

    • To Java字节码(LuaJC, lua-to-java-bytecode compiler)

    Luaj中的Globals对象不是线程安全的, 因此最佳实践是每个线程一个Globals对象。

    事实上, 可以采用ThreadLocal的方式来存储该对象。

    2)性能问题

    Lua脚本在JAVA中运行,相比于直接运行Java代码会慢很多,大约1000倍。

    四、Redis + Lua(EVAL命令)

    在使用Redisson、Jedis+Lua时,我们可以通过redis客户端集成的、手写的LUA脚本来保证一系列命令在Redis中可以"原子执行"。

    在redis执行lua脚本时,相当于一个redis级别的锁,不能执行其他操作,类似于原子操作,这也是redisson实现的一个关键点。

    比如Redisson中的lua脚本:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    Redisson如何实现分布式锁,可以看文章:https://www.yisu.com/article/277312.htm

    lua脚本中有如下几个概念:

    • redis.call():执行redis命令。

    • KEYS[n]:指脚本中第n个参数,比如KEYS[1]指脚本中的第一个参数。

    • ARGV[n]:指脚本中第n个参数的值,比如ARGV[1]指脚本中的第一个参数的值。

    • 返回值中nil与false同一个意思。

    1、EVAL命令

    redis2.6.0版本起 采用内置的Lua解释器 通过EVAL命令去执行脚本;

    redis中的EVAL命令可以用于执行一段lua代码。命令格式如下:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    • 第一个参数script:表示lua脚本的内容;

    • 第二参数numkeys:表示有多少个键值对。

    • 其余参数:先把numkeys个key列出来,再把numkeys个arg列出来。

    Lua脚本中可以使用2个函数调用redis命令;

    • redis.call()

    • redis.pcall()

    redis.call()与redis.pcall()相似,二者唯一不同之处:

    • 如果执行的redis命令执行失败,redis.call()将产生一个Lua error,从而迫使EVAL命令返回一个错误给命令的调用者;

    • 然而redis.pcall()将会捕捉这个错误,并返回代表这个错误的Lua表。

    有那么一段逻辑;

    • 如果Redis某个key的整数值 和 某个value相等,则将key对应的整数值 + 1000;否则将key的值设置为9999;

    lua脚本执行命令如下:

    EVAL "if redis.call(&#39;get&#39;, KEYS[1]) == ARGV[1] then return redis.call(&#39;INCRBY&#39;, KEYS[1], 1000); else redis.call(&#39;set&#39;, KEYS[1], 9999); return nil; end;" 1 test 100

    根据test值的不同,不同的执行结果如下:

    Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법

    위 내용은 Java 생태계/Redis에서 Lua 스크립트를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제