lua入门

redis2.6以后可以在redis 中使用lua语言。
1、用lua的好处:
1)一个脚本包含多个操作,减少访问次数从而减少网络开销
2)原子操作 redis 对lua脚本是原子化执行方案
3)复用性 复用 lua脚本的逻辑
2、lua脚本安装:
1)下载安装包 并解压

 tar-zvxf lua-5.3.5.tar.gz

2)执行 make linux 编译
编译过程报错 lua.c:82:31: 致命错误:readline/readline.h:没有那个文件或目录
百度一下得知,需要安装依赖
执行 如下命令安装依赖:

yum install libtermcap-devel ncurses-devel libevent-devel readline-devel

重新解压编译

[root@192 lua-5.3.5]# make linux test
cd src && make linux
make[1]: 进入目录“/usr/apps/lua/lua-5.3.5/src”
make all SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl -lreadline"
make[2]: 进入目录“/usr/apps/lua/lua-5.3.5/src”
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_USE_LINUX    -c -o lapi.o lapi.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_2 -DLUA_USE_LINUX    -c -o lcode.o lcode.c

然后 运行 lua 就启动进入了 lua 如下:

[root@192 ~]# lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> 

3、初步使用
lua特点 动态类型的语言,和js类似
变量 分为 全局变量 和局部变量

单行注释 –
多行注释 --[[ ]]

全局变量 a=1
局部变量 local b=1
逻辑表达式 ±*/
测试代码如下 编辑脚本 luatest :
vi testlua 输入内容 (lua testlua 执行脚本)

a=1
local b=2
print(a+b)

执行 lua luatest 结果
在这里插入图片描述

关系运算符
a==b 比较两个值是否相等
~= 比较两个值是否不等

a=1
local b=2
--print(a+b)
print (a==b)
print (a~=b)
print(a=='1')

执行 lua luatest 结果
在这里插入图片描述

逻辑运算符
and
or
not

测试脚本:

true
false
[root@192 lua-5.3.5]# vi luatest
print((a==b)and(a==1
a=1
local b=2
--[[print(a+b)
print (a==b)
print (a~=b)
print(a=='1')
]]
print((a==b)and(a==1) )
print((a==b)or(a==1) )
print(not(a==b))

执行 lua luatest 结果
在这里插入图片描述

字符串 操作
… 拼接 a…b 拼接字符串 a和b 同 java 的a+b
#计算字符串的长度 #a 字符串a 的长度
测试代码

str1='hello'
str2='world'
print(str1..str2)
print(#str1)

在这里插入图片描述

条件判断 如下
a=1
if( a == 1) then
print(‘a=1’)
elseif( b == 2) then
print(‘b=2’)
else
print(‘111’)
end

执行结果:
a=1

循环
a=1
while(a<10) do
print(a)
a=a+1
end
测试结果:
在这里插入图片描述

for i=1,5 do
print(i)
end
测试
在这里插入图片描述

遍历数组

local arr={‘a’,‘b’,‘c’}
for i,v in ipairs(arr) do
print(i…‘=’…v)
end
[root@192 lua-5.3.5]# lua luatest
1=a
2=b
3=c
[root@192 lua-5.3.5]#

测试结果如下

local function add(a,b)
return a+b;
end
print(add(1,2))
"luatest" 46L, 474C written
[root@192 lua-5.3.5]# lua luatest 
3

其他内置操作:Sting 操作字符串 Table 操作数组

4、redis 整合 lua

lua 提供了redis 的操作 如:redis.call(‘set‘,‘name’,‘zhang’)
但是直接运行该段脚本提示错误 如下

> redis.call('set','name','zhang');
stdin:1: attempt to index global 'redis' (a nil value)
stack traceback:
        stdin:1: in main chunk 
        [C]:

原因是直接在lua环境中运行该脚本,没有依赖的redis引擎。所以报错。
因为redis中提供了这个引擎,所以在redis中运行该段脚本就没有这个问题了。
在redis-cli 中运行该段脚本的方式: eval “脚本” keynumbers key… arg…[参数(没有参数写0)]
如下:

127.0.0.1:6379> eval "redis.call('set','name','zhang')" 0
(nil)
127.0.0.1:6379> get name
"zhang"
127.0.0.1:6379> 

有参数的测试:
通过 1(传入几个键) ‘name’ ‘lisi’ 出入参数
在脚本中 通过KEYS[1] 和 ARGV[1] 获取使用参数如下(下标从1开始):

127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 'name' 'lisi'
OK
127.0.0.1:6379> get name
"lisi"

5、lua实现访问ip频率限制
编写脚本

local num = redis.call('incr',KEYS[1])//每次ip访问的时候都对这个ip为键的值加1
if (tonumber(num)==1) then//如果是新插入的 ip (键在redis中不存在 自增 后返回1),那么为这个键设置一个过期时间 (过期时间由参数传入 比如下方测试中传入 10)
    redis.call('expire',KEYS[1],ARGV[1])  //设置过期时间
    return 1                                   // 返回1 表示没有 超限  (第一次访问肯定不超限)
elseif (tonumber(num)>tonumber(ARGV[2])) then //如果返回的结果不是1 证明这个ip 的键 在redis中已经存在(不是第一次访问) 判断是否大于最大限制值,如果大于返回0 表示已经超限。
    return 0
else//否则,返回1 表示不超限
    return 1
end

然后执行脚本 10秒内 第11次返回为0 (表示超限了,不允许访问了) 10秒后(因为过了限制的时间)返回为1,表示不超限了

[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10 , 10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 1
[root@192 bin]# ./redis-cli --eval "iplimitlua.lua" ip1 , 10  10
(integer) 0

遇到的问题:
执行lua脚本时 要 ./redis-cli --eval “iplimitlua.lua” ip1 , 10 10 这样来执行 (ip1 为ip地址key ,10 10 表示 10 秒内访问不能大于10次 并且逗号前后要有空格)
而不能 先执行 ./redis-cli
然后执行 -eval “iplimitlua.lua” ip1 , 10 10 这样会报错

6、lua脚本原子性验证
一个redis-cli 中执行一个死循环 如下:

[root@192 bin]# ./redis-cli
127.0.0.1:6379> eval "while true do print(1) end" 0

另一个客户端也链接 redis 执行redis操作 提示 redis is buzy .不能执行任何操作。

[root@192 bin]# ./redis-cli
127.0.0.1:6379> get name
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

只有杀死redis服务重启后才能正常访问redis

7、jedis 执行lua脚本
两种方式 一种是直接执行脚本
一种是 将脚本缓存在服务端,以摘要的方式执行,避免每次都传递完整脚本到服务端。具体脚本如下:



public class TestLUA {
	public static String lua="local num = redis.call('incr',KEYS[1]) "+
							"if (tonumber(num)==1) then "+
							"    redis.call('expire',KEYS[1],ARGV[1]) "+
							"    return 1 "+
							"elseif (tonumber(num)>tonumber(ARGV[2])) then "+
							"    return 0 "+
							"else "+
							"    return 1 "+ 
							"end";
	public static String sha =null;
	public static String loadScript() throws Exception{
		//
		Jedis jedis=RedisManager.getJedis();
		//将脚本缓存到服务端并返回摘要信息
		String sha = jedis.scriptLoad(lua);
		System.out.println("sha===>"+sha);
		return sha;
	}
	
	public static Object testLua1() throws Exception{
		if(null==sha){
			System.out.println("加载脚本");
			sha=loadScript();
		}
		
		Jedis jedis=RedisManager.getJedis();
		//String sha = jedis.scriptLoad(lua);
		List<String> keys = new ArrayList<String>();
		keys.add("ip:limit:127.0.0.1");
		
		List<String> args = new ArrayList<String>();
		args.add("30");
		args.add("10");
		//以摘要方式执行脚本
		return jedis.eval(sha, keys, args); 
		
	}
	
	public static Object testLua2() throws Exception{
		
		Jedis jedis=RedisManager.getJedis();
		List<String> keys = new ArrayList<String>();
		keys.add("ip:limit:127.0.0.1");
		
		List<String> args = new ArrayList<String>();
		args.add("30");
		args.add("10");
		
		return jedis.eval(lua, keys, args); //这样每次都传递脚本到服务端增加网络消耗
		
	}
	
	public static void main(String[] args) throws Exception {
		for(int i=0;i<13;i++){
			System.out.println(testLua1());
			//System.out.println(testLua2());

		}
		
	}

}

  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

catch that elf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值