SpringBoot通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log(方法步驟詳解)
第一次寫Lua腳本,并通過springboot的redisTemplate調(diào)用腳本,進(jìn)行腳本與應(yīng)用的交互。不熟悉真的折騰了好久,現(xiàn)在總結(jié)一下學(xué)習(xí)過程:
第一次寫完lua時(shí),想到的就是如何在應(yīng)用調(diào)用腳本的時(shí)候,去調(diào)試腳本。在網(wǎng)上海搜了一把,能找到的有點(diǎn)相關(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進(jìn)行命令調(diào)試的必要參數(shù)
--eval:告訴redis客戶端去加載Lua腳本,后面跟著的就是 lua腳本的路徑(我是直接放在redis目錄下),
1:傳給Lua腳本的key的數(shù)量,我測試的時(shí)候是1
--mykey:自己傳的一個(gè)key值,和前面的數(shù)量1對(duì)應(yīng)
--myargv:自己傳的除key外的參數(shù),可以是多個(gè)
注,命令中的逗號(hào)不能忽略,并且前后要有一個(gè)空格
回車,如上圖,本來以為可以進(jìn)入調(diào)試,結(jié)果等了半天,一直沒有出現(xiàn)交互的命令行界面,找了好久,還是沒找到辦法,結(jié)果只好先暫停(如果有大神遇到這種情況,跪求解~~)。換一種調(diào)試方式,把調(diào)試信息打在redis日志上。
下面是我自己調(diào)用腳本時(shí),打印調(diào)試信息的方式,如果有更好的方式,請(qǐng)不吝賜教。
1、選擇redisTemplate序列化方式
首先,創(chuàng)建一個(gè)redisTemplate,具體代碼就不說了,這個(gè)比較簡單。要注意的是,需要設(shè)置redisTemplate的序列化方式,springBoot默認(rèn)是基于java jdk的序列化。通過這種序列化后的參數(shù)傳到Lua腳本是,是無法正常打印到redis日志的,會(huì)出現(xiàn)亂碼,而且參數(shù)如果傳的是一個(gè)Map或List的話,不方便解析。并且這種序列化占用的字節(jié)比較大。所以改成JSON序列化,用FastJson實(shí)現(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ù),這個(gè)泛型類在使用時(shí)設(shè)置泛型是什么類型,腳本返回的結(jié)果就是用什么類型接收。注意,該類只接收4種類型的返回類型,之前沒注意,還納悶為什么出錯(cuò),看源碼才曉得,截圖,如下:
在lua腳本中,有兩個(gè)全局的變量,是用來接收redis應(yīng)用端傳遞的鍵值和其它參數(shù)的,分別為KEYS、ARGV。
在應(yīng)用端傳遞給KEYS時(shí)是一個(gè)數(shù)組列表,在lua腳本中通過索引方式獲取數(shù)組內(nèi)的值。
在應(yīng)用端,傳遞給ARGV的參數(shù)比較靈活,可以是多個(gè)獨(dú)立的參數(shù),但對(duì)應(yīng)到Lua腳本中是,統(tǒng)一用ARGV這個(gè)數(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ā)送了兩個(gè)key,還有一個(gè)map包裝的argv,傳遞到Lua腳本中時(shí),KEYS和ARGV接收到的是對(duì)象字符串,所以得用lua的庫做相關(guān)的解碼,我們發(fā)送的時(shí)候是用json序列化的,用Lua的庫cjson可以轉(zhuǎn)成json對(duì)象。下面貼上Lua腳本代碼:
--獲取KEY local key1 = KEYS[1] local key2 = KEYS[2] -- 獲取ARGV[1],這里對(duì)應(yīng)到應(yīng)用端是一個(gè)List<Map>. -- 注意,這里接收到是的字符串,所以需要用csjon庫解碼成table類型 local receive_arg_json = cjson.decode(ARGV[1]) --返回的變量 local result = {} --打印日志到reids --注意,這里的打印日志級(jí)別,需要和redis.conf配置文件中的日志設(shè)置級(jí)別一致才行 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) --用一個(gè)臨時(shí)變量來存放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對(duì)象,需要將json對(duì)象轉(zhuǎn)成字符串,客戶端才能接收 result[2] = ARGV[1] --將源參數(shù)內(nèi)容一起返回 redis.log(redis.LOG_DEBUG,cjson.encode(result)) --打印返回的數(shù)組結(jié)果,這里返回需要以字符返回 return result
3、設(shè)置日志級(jí)別
代碼中,redis.log()函數(shù)向運(yùn)日志中輸出信息,這里要注意一下,函數(shù)里面設(shè)置的日志級(jí)別要和redis.conf配置文件中設(shè)置的日志級(jí)別一樣才能正常打印到文件,這里我是設(shè)置成了deubg級(jí)別。這里可設(shè)置的級(jí)別有4種,分別如下:
- redis.LOG_DEBUG
- redis.LOG_VERBOSE
- redis.LOG_NOTICE
- redis.LOG_WARNING
在應(yīng)用端,我們?cè)O(shè)置接收返回的數(shù)據(jù)類型是List,所以在Lua腳本中,返回的類型用table與之對(duì)應(yīng),并且放到table變量中的內(nèi)容,得是字符串,應(yīng)用端才能通過反序列化,正常解析。下圖是輸出lua返回值的打印信息:
至此,結(jié)束,希望也能對(duì)其遇到相同問題的朋友有所幫助。
總結(jié)
以上所述是小編給大家介紹的SpringBoot通過redisTemplate調(diào)用lua腳本并打印調(diào)試信息到redis log(方法步驟詳解),希望對(duì)大家有所幫助!
- SpringBoot+Redis執(zhí)行l(wèi)ua腳本的5種方式總結(jié)
- Springboot+Redis執(zhí)行l(wèi)ua腳本的項(xiàng)目實(shí)踐
- springboot使用redisTemplate操作lua腳本
- springboot中使用redis并且執(zhí)行調(diào)試lua腳本
- SpringBoot通過RedisTemplate執(zhí)行Lua腳本的方法步驟
- SpringBoot+Redis執(zhí)行l(wèi)ua腳本的方法步驟
- SpringBoot利用注解來實(shí)現(xiàn)Redis分布式鎖
- SpringBoot基于Redis的分布式鎖實(shí)現(xiàn)過程記錄
- 關(guān)于SpringBoot 使用 Redis 分布式鎖解決并發(fā)問題
- springboot+redis+lua實(shí)現(xiàn)分布式鎖的腳本
相關(guān)文章
淺談Java實(shí)體對(duì)象的三種狀態(tài)以及轉(zhuǎn)換關(guān)系
這篇文章主要介紹了淺談Java實(shí)體對(duì)象的三種狀態(tài)以及轉(zhuǎn)換關(guān)系,具有一定參考價(jià)值,需要的朋友可以,看看。。2017-11-11Java并發(fā)編程中的CyclicBarrier使用解析
這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier使用解析,CyclicBarrier從字面意思上來看,循環(huán)柵欄,這篇文章就來分析下是到底是如何實(shí)現(xiàn)循環(huán)和柵欄的,需要的朋友可以參考下2023-12-12Java中類轉(zhuǎn)json的基類實(shí)現(xiàn)
這篇文章主要介紹了Java中類轉(zhuǎn)json的基類實(shí)現(xiàn),需要的朋友可以參考下2021-01-01java實(shí)現(xiàn)學(xué)生宿舍系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生宿舍系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03java 取交集方法retainAll的實(shí)現(xiàn)
這篇文章主要介紹了java 取交集方法retainAll的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06JAVA如何判斷上傳文件后綴名是否符合規(guī)范MultipartFile
這篇文章主要介紹了JAVA判斷上傳文件后綴名是否符合規(guī)范MultipartFile,文中通過實(shí)例代碼介紹了java實(shí)現(xiàn)對(duì)上傳文件做安全性檢查,需要的朋友可以參考下2023-11-11如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗(yàn)
這篇文章主要介紹了如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗(yàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07