Welcome everyone

lua 学习笔记

linux 汪明鑫 939浏览 0评论

什么是lua

Lua是一个高效的轻量级脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能

 

优势

  • 减少网络开销:在Lua脚本中可以把多个命令放在同一个脚本中运行
  • 原子操作:redis会将整个脚本作为一个整体执行,中间不会被其他命令插入
  • 复用性:客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑

 

应用场景

  • 游戏开发
  • 独立应用脚本
  • Web 应用脚本
  • 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
  • 安全系统,如入侵检测系统

 

lua安装

curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxf lua-5.3.0.tar.gz
cd lua-5.3.0
make linux test
make install

执行make linux test时,可能会报下面的错

 

安装依赖后重新执行即可

[root@xinyeshuaiqi lua]# yum install readline-devel -y

 

有下面提示信息表示安装成功

[root@xinyeshuaiqi lua]# lua -v
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio

 

lua交互式编程

[root@xinyeshuaiqi lua]# lua -i
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> print("hello world")
hello world

 

lua脚本式编程

vi hello.lua

print("xinye ")
print("shuaiqi")

 

执行脚本

[root@xinyeshuaiqi lua]# lua hello.lua 
xinye 
shuaiqi

 

lua基础语法

定义变量:

[root@xinyeshuaiqi lua]# lua -i
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> a=1
> print(a)

 

删除变量:

> a = nil
> print(a)
nil

置为nil即可

 

判等:

> b=1
> a==b
true

> c =2
> a~=c
true

~= 不等于

> 1=='1'
false

不会自动转型

 

连接符:

> a..c
12

 

注释:

单行注释

-- 单行注释

多行注释

--[[
 多行注释
 --]]

 

 

lua数据类型:

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

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

数据类型 描述
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。

 

type 函数测试给定变量或者值的类型:

> print(type("Hello world")) 
string
> print(type("Hello world")) 
string
> print(type(10.4*3))
number
> print(type(print)) 
function
> print(type(type))
function
> print(type(true)) 
boolean
> print(type(nil))
nil
> print(type(type(X)))
string

 

 

循环:

[root@xinyeshuaiqi lua]# vi test.lua 
for i=1,100 do
  print(i)
end

while 循环差不多

 

if-else:

a=5

if(a<10)
then
  print("a<10")
else
  print("a>=10")
end

print("a=",a)

 

函数:

function add(a,b)
 return a+b
end

print(add(1,3))

 

lua与redis

 

 

Redis 创建了用于与 Lua 环境协作的组件—— 伪客户端,它负责执行 Lua 脚本中的 Redis 命令。

 

在 Redis 内置的 Lua 解析器中,调用 redis.call() 和 redis.pcall() 函数执行 Redis 的命令

 

eval的使用:

127.0.0.1:6667> EVAL "return redis.call('SET', 'name', 'xinye')" 0  
OK
127.0.0.1:6667> EVAL "return redis.call('GET', 'name')" 0
"xinye"
127.0.0.1:6667> EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 haha hehe  
OK
127.0.0.1:6667> get haha
"hehe"

KEYS[1] 第一个键值,ARGV[1]第一个键对应的值

haha hehe 是实际的值

后面跟的numbers,如0或1,表示key的个数

 

EVALSHA:

eval执行lua脚本,脚本比较长的情况下,每次调用脚本都需要把整个脚本传给redis,比较占用带宽。为了解决这个问题,redis提供了EVALSHA命令允许开发者通过脚本内容的SHA1摘要来执行脚本。该命令的用法和EVAL一样,只不过是将脚本内容替换成脚本内容的SHA1摘要
127.0.0.1:6667> script load "return redis.call('get','haha')"
"975c5443120508176af91615e70db444745d83bf"
127.0.0.1:6667> evalsha 975c5443120508176af91615e70db444745d83bf 0
"hehe"

 

Java应用程序使用lua

写一个简单的Spring Boot整合lua的例子

模拟一哈CAS

 

先写一个脚本:

local current = redis.call('GET', KEYS[1])
if current == ARGV[1]
  then redis.call('SET', KEYS[1], ARGV[2])
  return true
end
return false

 

redis配置类:

package pers.wmx.springbootfreemarkerdemo;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author wmx
 * @date 2019-12-10
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory ) {
        //设置序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        // key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value序列化
        redisTemplate.setValueSerializer(stringSerializer);
        // Hash key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash value序列化
        redisTemplate.setHashValueSerializer(stringSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

 

加载lua脚本

package pers.wmx.springbootfreemarkerdemo;

import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

/**
 * @author wmx
 * @date 2019-12-10
 */
@Component
public class LuaUtil {
    @Bean
    DefaultRedisScript<Boolean> luaScript() {
        DefaultRedisScript<Boolean> luaScript = new DefaultRedisScript<>();
        luaScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("test.lua")));
        luaScript.setResultType(Boolean.class);
        return luaScript;
    }
}

 

写一个单测:

package pers.wmx.springbootfreemarkerdemo;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Collections;

/**
 * @author wmx
 * @date 2019-12-10
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class luaTest {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    DefaultRedisScript<Boolean> luaScript;

    @Test
    public void testLua(){
        String key = "testLua";

        redisTemplate.delete(key);
        redisTemplate.opsForValue().set(key, "test CAS");
        String s = redisTemplate.opsForValue().get(key).toString();
        System.out.println(s);

        redisTemplate.execute(luaScript, Collections.singletonList(key), "test CAS", "666"); 
        //redisTemplate.execute(luaScript, Collections.singletonList(key), "test CAS 1", "666"); 
        s = redisTemplate.opsForValue().get(key).toString();
        System.out.println(s);
    }
}

 

redisTemplate.execute(luaScript, Collections.singletonList(key), "test CAS 1", "666");

test CAS 1 与 redis存的值test CAS不同,所以设置新值失败

 

参考

https://www.runoob.com/lua/lua-tutorial.html

https://www.cnblogs.com/PatrickLiu/p/8391829.html

https://www.fanhaobai.com/2017/09/lua.html

转载请注明:汪明鑫的个人博客 » lua 学习笔记

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz