溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log

發(fā)布時間:2021-05-07 10:18:55 來源:億速云 閱讀:288 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關(guān)SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

springboot是什么

springboot一種全新的編程規(guī)范,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程,SpringBoot也是一個服務(wù)于框架的框架,服務(wù)范圍是簡化配置文件。

第一次寫Lua腳本,并通過springboot的redisTemplate調(diào)用腳本,進行腳本與應(yīng)用的交互。不熟悉真的折騰了好久,現(xiàn)在總結(jié)一下學(xué)習(xí)過程:

第一次寫完lua時,想到的就是如何在應(yīng)用調(diào)用腳本的時候,去調(diào)試腳本。在網(wǎng)上海搜了一把,能找到的有點相關(guān)的寥寥無幾。

有一種方法是通過執(zhí)行redis命令,調(diào)用redis客戶端,加載lua腳本,然后出現(xiàn)基于命令行調(diào)試的交互界面,輸入調(diào)試命令去調(diào)試腳本。如下:

在終端輸入命令:redis-cli.exe --ldb --eval LimitLoadTimes.lua 1 mykey , myargv

--ldb:redis-cli.exe進行命令調(diào)試的必要參數(shù)

--eval:告訴redis客戶端去加載Lua腳本,后面跟著的就是 lua腳本的路徑(我是直接放在redis目錄下),

1:傳給Lua腳本的key的數(shù)量,我測試的時候是1

--mykey:自己傳的一個key值,和前面的數(shù)量1對應(yīng)

--myargv:自己傳的除key外的參數(shù),可以是多個

注,命令中的逗號不能忽略,并且前后要有一個空格

SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log

回車,如上圖,本來以為可以進入調(diào)試,結(jié)果等了半天,一直沒有出現(xiàn)交互的命令行界面,找了好久,還是沒找到辦法,結(jié)果只好先暫停(如果有大神遇到這種情況,跪求解~~)。換一種調(diào)試方式,把調(diào)試信息打在redis日志上。

下面是我自己調(diào)用腳本時,打印調(diào)試信息的方式,如果有更好的方式,請不吝賜教。

1、選擇redisTemplate序列化方式

首先,創(chuàng)建一個redisTemplate,具體代碼就不說了,這個比較簡單。要注意的是,需要設(shè)置redisTemplate的序列化方式,springBoot默認(rèn)是基于java jdk的序列化。通過這種序列化后的參數(shù)傳到Lua腳本是,是無法正常打印到redis日志的,會出現(xiàn)亂碼,而且參數(shù)如果傳的是一個Map或List的話,不方便解析。并且這種序列化占用的字節(jié)比較大。所以改成JSON序列化,用FastJson實現(xiàn)。

下面貼上redis序列化代碼:

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
 public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
 private Class<T> clazz;
 public FastJsonRedisSerializer(Class<T> clazz){
  super();
  this.clazz = clazz;
 }
 @Override
 public byte[] serialize(T t) throws SerializationException {
  return ofNullable(t)
    .map(r -> JSON.toJSONString(r, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET))
    .orElseGet(() -> new byte[0]);
 }
 @Override
 public T deserialize(byte[] bytes) throws SerializationException {
  return Optional.ofNullable(bytes)
    .map(t -> JSON.parseObject(new String(t, DEFAULT_CHARSET), clazz))
    .orElseGet(() -> null);
 }
}

2、應(yīng)用端加載腳本,并設(shè)置傳遞參數(shù)

在springboot中,是用 DefaultRedisScript 類來加載腳本的,并設(shè)置相應(yīng)的數(shù)據(jù)類型來接收lua腳本返回的數(shù)據(jù),這個泛型類在使用時設(shè)置泛型是什么類型,腳本返回的結(jié)果就是用什么類型接收。注意,該類只接收4種類型的返回類型,之前沒注意,還納悶為什么出錯,看源碼才曉得,截圖,如下:

SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log

在lua腳本中,有兩個全局的變量,是用來接收redis應(yīng)用端傳遞的鍵值和其它參數(shù)的,分別為KEYS、ARGV。

在應(yīng)用端傳遞給KEYS時是一個數(shù)組列表,在lua腳本中通過索引方式獲取數(shù)組內(nèi)的值。

在應(yīng)用端,傳遞給ARGV的參數(shù)比較靈活,可以是多個獨立的參數(shù),但對應(yīng)到Lua腳本中是,統(tǒng)一用ARGV這個數(shù)組接收,獲取方式也是通過數(shù)組下標(biāo)獲取。

下面貼上應(yīng)用端的測試代碼:

@Service("luaScriptService")
public class LuaScriptServiceImpl implements LuaScriptService{
 @Autowired
 private RedisTemplate<String,Object> redisTemplate1;
 private DefaultRedisScript<List> getRedisScript;
 @PostConstruct
 public void init(){
  getRedisScript = new DefaultRedisScript<List>();
  getRedisScript.setResultType(List.class);
  getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("luascript/LimitLoadTimes.lua")));
 }
 @Override
 public void redisAddScriptExec(){
  /**
   * List設(shè)置lua的KEYS
   */
  List<String> keyList = new ArrayList();
  keyList.add("count");
  keyList.add("rate.limiting:127.0.0.1");
  /**
   * 用Mpa設(shè)置Lua的ARGV[1]
   */
  Map<String,Object> argvMap = new HashMap<String,Object>();
  argvMap.put("expire",10000);
  argvMap.put("times",10);
  /**
   * 調(diào)用腳本并執(zhí)行
   */
  List result = redisTemplate1.execute(getRedisScript,keyList, argvMap);
  System.out.println(result);
 }
}

代碼中發(fā)送了兩個key,還有一個map包裝的argv,傳遞到Lua腳本中時,KEYS和ARGV接收到的是對象字符串,所以得用lua的庫做相關(guān)的解碼,我們發(fā)送的時候是用json序列化的,用Lua的庫cjson可以轉(zhuǎn)成json對象。下面貼上Lua腳本代碼:

--獲取KEY
local key1 = KEYS[1]
local key2 = KEYS[2]
-- 獲取ARGV[1],這里對應(yīng)到應(yīng)用端是一個List<Map>.
-- 注意,這里接收到是的字符串,所以需要用csjon庫解碼成table類型
local receive_arg_json = cjson.decode(ARGV[1])
--返回的變量
local result = {}
--打印日志到reids
--注意,這里的打印日志級別,需要和redis.conf配置文件中的日志設(shè)置級別一致才行
redis.log(redis.LOG_DEBUG,key1)
redis.log(redis.LOG_DEBUG,key2)
redis.log(redis.LOG_DEBUG, ARGV[1],#ARGV[1])
--獲取ARGV內(nèi)的參數(shù)并打印
local expire = receive_arg_json.expire
local times = receive_arg_json.times
redis.log(redis.LOG_DEBUG,tostring(times))
redis.log(redis.LOG_DEBUG,tostring(expire))
--往redis設(shè)置值
redis.call("set",key1,times)
redis.call("incr",key2)
redis.call("expire",key2,expire)
--用一個臨時變量來存放json,json是要放入要返回的數(shù)組中的
local jsonRedisTemp={}
jsonRedisTemp[key1] = redis.call("get",key1)
jsonRedisTemp[key2] = redis.call("get", key2)
jsonRedisTemp["ttl"] = redis.call("ttl",key2)
redis.log(redis.LOG_DEBUG, cjson.encode(jsonRedisTemp))
result[1] = cjson.encode(jsonRedisTemp) --springboot redistemplate接收的是List,如果返回的數(shù)組內(nèi)容是json對象,需要將json對象轉(zhuǎn)成字符串,客戶端才能接收
result[2] = ARGV[1] --將源參數(shù)內(nèi)容一起返回
redis.log(redis.LOG_DEBUG,cjson.encode(result)) --打印返回的數(shù)組結(jié)果,這里返回需要以字符返回
return result

3、設(shè)置日志級別

代碼中,redis.log()函數(shù)向運日志中輸出信息,這里要注意一下,函數(shù)里面設(shè)置的日志級別要和redis.conf配置文件中設(shè)置的日志級別一樣才能正常打印到文件,這里我是設(shè)置成了deubg級別。這里可設(shè)置的級別有4種,分別如下:

  • redis.LOG_DEBUG

  • redis.LOG_VERBOSE

  • redis.LOG_NOTICE

  • redis.LOG_WARNING

在應(yīng)用端,我們設(shè)置接收返回的數(shù)據(jù)類型是List,所以在Lua腳本中,返回的類型用table與之對應(yīng),并且放到table變量中的內(nèi)容,得是字符串,應(yīng)用端才能通過反序列化,正常解析。下圖是輸出lua返回值的打印信息:

SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log

感謝各位的閱讀!關(guān)于“SpringBoot怎么通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI