用jedis执行lua脚本

1.Redis 脚本

Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。

下表列出了 redis 脚本常用命令:

序号命令及描述
1EVAL script numkeys key [key ...] arg [arg ...]

执行 Lua 脚本。

2EVALSHA sha1 numkeys key [key ...] arg [arg ...]

执行 Lua 脚本。

3SCRIPT EXISTS script [script ...]

查看指定的脚本是否已经被保存在缓存当中。

4SCRIPT FLUSH

从脚本缓存中移除所有脚本。

5SCRIPT KILL

杀死当前正在运行的 Lua 脚本。

6SCRIPT LOAD script

将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。

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

注意

1) app:ip:limit:192.168.1.15 是key值 ,后面是参数值,中间要加上一个空格 和 一个逗号,再加上一个 空格 。即:./redis-cli –eval [lua脚本] [key…]空格,空格[args…]

2) 多个参数之间用一个 空格 分割 。

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"

4.redis 和 java 整合

jedis.scriptLoad方法将script 脚本添加到脚本缓存中,如果脚本没有加载过,那么进行加载,这样就会返回一个sha1编码。

jedis.evalsha() 根据sha1编码 和 key value值执行脚本,返回结果。

依赖:

<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>

代码如下,

Jedis连接池:

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;
    }
}

测试实现类:

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);
   }       
}

参考:

https://zixuephp.net/manual-redis-2328.html

https://blog.csdn.net/xiaojin21cen/article/details/88621540

https://blog.csdn.net/weixin_38070406/article/details/78246786