【redis知识点整理】 --- linux中Redis客户端 + springboot项目中RedisTemplate如何调用lua脚本

本文代码对应的github地址:https://github.com/nieandsun/redis-study



1 redis脚本 & 事务

我在《【redis知识点整理】 — redis事务简介》那篇文章里翻译过Redis官网( https://redis.io/topics/transactions )的一段话,现再次贴在下面:

在这里插入图片描述
这段话啥意思呢?

  • (1) redis脚本可以做现在redis提供的事务的任何工作,且脚本方式更简单,更快
  • (2)之所以又有脚本方式、又有本文(《【redis知识点整理】 — redis事务简介》)介绍的这些方式,是因为本文介绍的内容很早就有了,脚本方式是Redis2.6才引入的
  • (3)或许未来所有使用redis的用户都只用脚本方式进行事务的操作,如果真是那样,redis官网就有可能弃用并删除本文介绍的事务方式。

从上面的翻译可以看出来,使用脚本方式操作redis将满足事务特性。 这里有三点我觉得必须得点出来:

  • (1)redis使用的脚本为Lua脚本
  • (2)Lua脚本的特性为脚本内的命令要么全部执行成功,要么全部失败
  • (3)基于(2)并与《【redis知识点整理】 — redis事务简介》那篇文章介绍的redis提供的事务进行对比可知:
    • redis使用MULTI开启的事务为弱事务,它虽然也是原子性的,但是强调的是事务内的命令要么全部执行,要么全部不执行
    • redis使用脚本方式运行的多条命令,将处于一个强事务内: 要么全部执行成功,要么全部失败!!!

两者是有本质区别的。


2 redis脚本的玩法 & Linux篇

事实上,我们其实根本不用安装Lua,因为redis本身就可以解析Lua脚本,至于Redis为啥要使用Lua作为脚本语言、以及Lua语言的语法等有兴趣的可以自行百度,比如说如下网站:

Lua官网: http://www.lua.org/
菜鸟教程:https://www.runoob.com/lua/lua-tutorial.html
redis官网关于脚本的介绍:https://redis.io/commands/eval

redis脚本主要有如下两种玩法。

2.1 玩法1 — EVAL

语法如下:

EVAL script numkeys key [key ...] arg [arg ...]
  • EVAL:表示redis使用字符串脚本
  • script:为具体的脚本
  • numkeys :操作的key的数量
  • key [key …]:具体要操作的key
  • arg [arg …] : 往脚本中传入的参数

举例如下:
在这里插入图片描述
上面的命令其实就相当于set name yoyo,由此其实可以看到,在脚本中:

  • KEYS[num] —> 表示接收的redis的key
  • ARGV[num] —>表示接收的redis的value

2.2 玩法2 — EVALSHA

语法如下:

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

可以看到该语法除了前两个指令外其他的和EVAL的语法都一样,这里简单介绍一下前两个指令的意思:

  • EVALSHA: 表示使用EVALSHA 的方式运行redis脚本(☺☺☺)
  • sha1 :其实指的是某个脚本生成的sha值

下面详细介绍某个脚本生成sha值的方法:

(1)首先肯定要开启redis服务器
在这里插入图片描述
(2)其次在redis的安装目录下(我的为/usr/local/software/redis/bin)写一个简单的redis脚本,比如说
在这里插入图片描述
(3)生成SHA值的语法如下

./redis-cli -h 192.168.65.135 -p 6379 -a 123456 script load "$(cat simple.lua )" 

上面的命令就是使用redis客户端在

  • 服务器为 192.168.65.135 <—> -h 192.168.65.135
  • 端口号 6379 <—> -p 6379
  • 密码为123456 <—> -a 123456

的redis服务器上为 simple.lua脚本生成sha值。

运行结果如下:
在这里插入图片描述


之后我们就可以直接拿着生成sha值进行操作了:
命令如下:

EVALSHA abdd7885574293da651b0a118b1552e42f334b6a 2 name age yoyo 18

接下来看看执行结果:在这里插入图片描述


3 springboot项目中RedisTemplate如何调用Lua脚本


3.1 RedisTemplate调用Lua脚本方法(execute)简介

我们首先看一下调用execute的构造方法:
在这里插入图片描述
从上面的图可以看出来,RedisTemplate调用Lua脚本的构造方法有两个,我把他们再单独拿出来稍微解释一下:

  • 第一种构造
@Nullable
//第一个参数,对脚本的一个封装
//第二个参数,key组成的list列表
//第三个参数,value组成的可变参数列表
//将其与上面讲的EVAL 或 EVALSHA方式执行LUA脚本的方式进行对比,应该比较好理解
<T> T execute(RedisScript<T> script, List<K> keys, Object... args); 
  • 第二种构造
	//可以看到多了两个参数,如果看过我上篇文章的话,应该对这两个参数并不陌生
	//RedisSerializer<?> argsSerializer -->指定Value的序列化方式
	//RedisSerializer<T> resultSerializer --> 指定返回结果的序列化方式
	@Nullable
	<T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer,
			List<K> keys, Object... args);

3.2 方式1 — 直接用字符串构造RedisScript

  • demo
    @GetMapping("/lua-demo")
    public String LuaDemo2() {
        //lua脚本
        String script = "local key1 = KEYS[1]\n" +
                "local key2 = KEYS[2]\n" +
                "local arg1 = ARGV[1]\n" +
                "local arg2 = tonumber(ARGV[2])\n" +
                "\n" +
                "redis.call(\"SET\", key1, arg1)\n" +
                "redis.call(\"lpush\",key2,arg2)\n" +
                "\n" +
                "return 1";

        // 构造RedisScript并指定返回类类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
        Long result = redisTemplate.execute(redisScript, Arrays.asList("name111", "age111"), "yoyo111", 19);
        System.out.println(result);
        return "OK";
    }
  • 测试结果如下:

这里要注意一下,name111之所以不是一个简单的字符串"yoyo",是因为我指定Value的序列化方式为Jackson2JsonRedisSerializer
在这里插入图片描述


3.3 方式2 — 读取lua脚本来构造RedisScript

比如说我将lua脚本放在了下面的目录下:
在这里插入图片描述
则可以按照如下的方式进行读取lua脚本、构造RedisScript对象

@GetMapping("/lua-limit")
public String LuaDemo() {
    // 构造RedisScript
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    // 指定要使用的lua脚本
    redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis-lua/ipcount.lua")));
    //指定返回类型
    redisScript.setResultType(Long.class);
    // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
    Long result = redisTemplate.execute(redisScript, Arrays.asList("127.0.0.1"), 5, 2);
    log.info("是否获可以访问:{}", result == 1 ? "是" : "否");
    return "OK";
}

简单用jmeter测试一下上面代码的限流效果

测试计划如下,即2秒内发送15个请求
在这里插入图片描述
测试结果如下,可以看到2秒内仅有5个请求可以访问,说明我们写的限流小demo可用
在这里插入图片描述


end!!!

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/106457247