Lua 脚本在redis中的入门使用——eval

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>

猜你喜欢

转载自blog.csdn.net/xiaojin21cen/article/details/88621540