1、eval 的基本使用
EVAL script numkeys key [key …] arg [arg …]
参数说明:
-
1、
script
参数是一段 Lua 5.1 脚本。它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。 -
2、
numkeys
参数表示键的个数。 -
3、
键名 key [key ...]
表示 键的值。在 Lua 中 通过全局变量KEYS
数组来指定键值, 从1 为开始,访问( KEYS[1] , KEYS[2] ,以此类推)。 -
4、
参数 arg [arg ...]
表示参数值。在 Lua 中通过全局变量ARGV
数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
92:0>eval "return redis.call('set',KEYS[1],ARGV[1])" 1 eoo bbb #执行lua脚本。1表示键的个数,eoo对应的是key值
"OK"
92:0>get eoo #查询 eoo 的值
"bbb"
说明:
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 eoo bbb
这个脚本相当于 set eoo bb
这个命令操作 。
2、lua 文件编写和执行
ip_limit.lua
:
-- IP限流,对某个IP频率进行限制 ,1分钟访问10次
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
执行 ip_limit.lua
脚本:
./redis-cli --eval "ip_limit.lua" app:ip:limit:192.168.1.15 , 6000 10
(integer) 1
注意
app:ip:limit:192.168.1.15
是key值 ,后面是参数值,中间要加上一个空格
和 一个逗号
,再加上一个空格
。
即:./redis-cli –eval [lua脚本] [key…]空格,空格[args…]
- 多个参数之间用一个
空格
分割 。
3、evalsha 的基本使用
每次使用 eval 执行很长的脚本其实没什么必要, redis可以将脚本缓存起来,生成一个脚本的 SHA1 ,标记这个这个脚本,然后 使用时传 SHA1 和key value 的值即可。
92:0>script load "return redis.call('set',KEYS[1],ARGV[1])" # 这段脚本 生成了SHA1 值,来标记其唯一性
"c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
92:0>evalsha c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1 AA BB # 使用 evalsha 命令 和 SHA1 值来执行脚本
"OK"
92:0>get AA # 验证数据存储是否成功
"BB"
redis 和 java 整合
testScriptLoad() 方法执行lua脚本得到返回值
testEvalsha() 根据返回值 和 key value值 执行。
public class LuaDemo{
@Test
public void testScriptLoad() {
JedisPool jedisPool = JedisPoolUtils.getInstance();
Jedis jedis = jedisPool.getResource();
System.out.println(jedis);
String lua = "local num = redis.call('incr', KEYS[1])\n" +
"if tonumber(num) == 1 then\n" +
"\tredis.call('expire', KEYS[1], ARGV[1])\n" +
"\treturn 1\n" +
"elseif tonumber(num) > tonumber(ARGV[2]) then\n" +
"\treturn 0\n" +
"else \n" +
"\treturn 1\n" +
"end\n";
String scriptLoad=jedis.scriptLoad(lua);
System.out.println(scriptLoad);
}
@Test
public void testEvalsha() {
JedisPool jedisPool = JedisPoolUtils.getInstance();
Jedis jedis = jedisPool.getResource();
try {
String scriptLoad ="5ae1f63a19ef16ea0d8c0268d01c5fa01312b7e6"; //来自上面的 testScriptLoad()的值
Object result = jedis.evalsha(scriptLoad , Arrays.asList("localhost"), Arrays.asList("10000", "2"));
System.out.println("aaa:"+result);
}catch (Exception e){
e.printStackTrace();
}finally {
if(jedis != null){
try {
jedis.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
@Test
public void script() throws InterruptedException {
JedisPool jedisPool = JedisPoolUtils.getInstance();
Jedis jedis = jedisPool.getResource();
List<String> keys = new ArrayList<>();
List<String> vals = new ArrayList<>();
// 测试1、 基本测试
Object eval = jedis.eval("return 1", keys, vals);
System.out.println(eval);
// 测试2、 eval 里面也可以是一个文件
keys.add("kk");
Object eval2 = jedis.eval("local tab={} for i=1,#KEYS do tab[i] = redis.call('get',KEYS[i]) end return tab",
keys, vals);
System.out.println(eval2);
// 测试3、 scriptLoad
//好处:这样可以缓存到服务器,不用每次把lua脚本的内容传过去
String lua = "local tab={} for i=1,#KEYS do tab[i] = redis.call('get',KEYS[i]) end return tab";
String scriptLoad = jedis.scriptLoad(lua);
System.out.println(scriptLoad);
Object evalsha = jedis.evalsha(scriptLoad, keys, vals);
System.out.println(evalsha);
}
}
public class JedisPoolUtils {
private static JedisPool jedisPool;
public static JedisPool getInstance() {
if (jedisPool == null) {
synchronized (JedisPoolUtils.class) {
if (jedisPool == null) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 4000, "123456");
}
}
}
return jedisPool;
}
}
依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>