SpringBoot如何通过redisTemplate调用lua脚本并打印调试信息到redislog
创始人
2024-12-25 04:42:24
0次

第一次写Lua脚本,并通过springboot的redisTemplate调用脚本,进行脚本与应用的交互。不熟悉真的折腾了好久,现在总结一下学习过程:

第一次写完lua时,想到的就是如何在应用调用脚本的时候,去调试脚本。在网上海搜了一把,能找到的有点相关的寥寥无几。

有一种方法是通过执行redis命令,调用redis客户端,加载lua脚本,然后出现基于命令行调试的交互界面,输入调试命令去调试脚本。如下:

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

--ldb:redis-cli.exe进行命令调试的必要参数

--eval:告诉redis客户端去加载Lua脚本,后面跟着的就是 lua脚本的路径(我是直接放在redis目录下),

1:传给Lua脚本的key的数量,我测试的时候是1

--mykey:自己传的一个key值,和前面的数量1对应

--myargv:自己传的除key外的参数,可以是多个

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

回车,如上图,本来以为可以进入调试,结果等了半天,一直没有出现交互的命令行界面,找了好久,还是没找到办法,结果只好先暂停(如果有大神遇到这种情况,跪求解~~)。换一种调试方式,把调试信息打在redis日志上。

下面是我自己调用脚本时,打印调试信息的方式,如果有更好的方式,请不吝赐教。

1、选择redisTemplate序列化方式

首先,创建一个redisTemplate,具体代码就不说了,这个比较简单。要注意的是,需要设置redisTemplate的序列化方式,springBoot默认是基于java jdk的序列化。通过这种序列化后的参数传到Lua脚本是,是无法正常打印到redis日志的,会出现乱码,而且参数如果传的是一个Map或List的话,不方便解析。并且这种序列化占用的字节比较大。所以改成JSON序列化,用FastJson实现。

下面贴上redis序列化代码:

public class FastJsonRedisSerializer implements RedisSerializer { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class clazz; public FastJsonRedisSerializer(Class 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、应用端加载脚本,并设置传递参数

在springboot中,是用 DefaultRedisScript 类来加载脚本的,并设置相应的数据类型来接收lua脚本返回的数据,这个泛型类在使用时设置泛型是什么类型,脚本返回的结果就是用什么类型接收。注意,该类只接收4种类型的返回类型,之前没注意,还纳闷为什么出错,看源码才晓得,截图,如下:

在lua脚本中,有两个全局的变量,是用来接收redis应用端传递的键值和其它参数的,分别为KEYS、ARGV。

在应用端传递给KEYS时是一个数组列表,在lua脚本中通过索引方式获取数组内的值。

在应用端,传递给ARGV的参数比较灵活,可以是多个独立的参数,但对应到Lua脚本中是,统一用ARGV这个数组接收,获取方式也是通过数组下标获取。

下面贴上应用端的测试代码:

@Service("luaScriptService")public class LuaScriptServiceImpl implements LuaScriptService{ @Autowired private RedisTemplate redisTemplate1; private DefaultRedisScript getRedisScript; @PostConstruct public void init(){  getRedisScript = new DefaultRedisScript();  getRedisScript.setResultType(List.class);  getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("luascript/LimitLoadTimes.lua"))); } @Override public void redisAddScriptExec(){  /**   * List设置lua的KEYS   */  List keyList = new ArrayList();  keyList.add("count");  keyList.add("rate.limiting:127.0.0.1");  /**   * 用Mpa设置Lua的ARGV[1]   */  Map argvMap = new HashMap();  argvMap.put("expire",10000);  argvMap.put("times",10);  /**   * 调用脚本并执行   */  List result = redisTemplate1.execute(getRedisScript,keyList, argvMap);  System.out.println(result); }}

代码中发送了两个key,还有一个map包装的argv,传递到Lua脚本中时,KEYS和ARGV接收到的是对象字符串,所以得用lua的库做相关的解码,我们发送的时候是用json序列化的,用Lua的库cjson可以转成json对象。下面贴上Lua脚本代码:

--获取KEYlocal key1 = KEYS[1]local key2 = KEYS[2]-- 获取ARGV[1],这里对应到应用端是一个List.-- 注意,这里接收到是的字符串,所以需要用csjon库解码成table类型local receive_arg_json = cjson.decode(ARGV[1])--返回的变量local result = {}--打印日志到reids--注意,这里的打印日志级别,需要和redis.conf配置文件中的日志设置级别一致才行redis.log(redis.LOG_DEBUG,key1)redis.log(redis.LOG_DEBUG,key2)redis.log(redis.LOG_DEBUG, ARGV[1],#ARGV[1])--获取ARGV内的参数并打印local expire = receive_arg_json.expirelocal times = receive_arg_json.timesredis.log(redis.LOG_DEBUG,tostring(times))redis.log(redis.LOG_DEBUG,tostring(expire))--往redis设置值redis.call("set",key1,times)redis.call("incr",key2)redis.call("expire",key2,expire)--用一个临时变量来存放json,json是要放入要返回的数组中的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,如果返回的数组内容是json对象,需要将json对象转成字符串,客户端才能接收result[2] = ARGV[1] --将源参数内容一起返回redis.log(redis.LOG_DEBUG,cjson.encode(result)) --打印返回的数组结果,这里返回需要以字符返回return result

3、设置日志级别

代码中,redis.log()函数向运日志中输出信息,这里要注意一下,函数里面设置的日志级别要和redis.conf配置文件中设置的日志级别一样才能正常打印到文件,这里我是设置成了deubg级别。这里可设置的级别有4种,分别如下:

redis.LOG_DEBUG  redis.LOG_VERBOSE  redis.LOG_NOTICE  redis.LOG_WARNING

在应用端,我们设置接收返回的数据类型是List,所以在Lua脚本中,返回的类型用table与之对应,并且放到table变量中的内容,得是字符串,应用端才能通过反序列化,正常解析。下图是输出lua返回值的打印信息:

至此,结束,希望也能对其遇到相同问题的朋友有所帮助。

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...