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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章