SpringBoot通过redisTemplate调用lua脚本并打印调试信息到redis log(方法步骤详解)

第一次写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<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、应用端加载脚本,并设置传递参数

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

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

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

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

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

@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设置lua的KEYS
   */
  List<String> keyList = new ArrayList();
  keyList.add("count");
  keyList.add("rate.limiting:127.0.0.1");
  /**
   * 用Mpa设置Lua的ARGV[1]
   */
  Map<String,Object> argvMap = new HashMap<String,Object>();
  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脚本代码:

--获取KEY
local key1 = KEYS[1]
local key2 = KEYS[2]
-- 获取ARGV[1],这里对应到应用端是一个List<Map>.
-- 注意,这里接收到是的字符串,所以需要用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.expire
local times = receive_arg_json.times
redis.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返回值的打印信息:

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

总结

以上所述是小编给大家介绍的SpringBoot通过redisTemplate调用lua脚本并打印调试信息到redis log(方法步骤详解),希望对大家有所帮助!

(0)

相关推荐

  • Spring boot项目redisTemplate实现轻量级消息队列的方法

    背景 公司项目有个需求, 前端上传excel文件, 后端读取数据.处理数据.返回错误数据, 最简单的方式同步处理, 客户端上传文件后一直阻塞等待响应, 但用户体验无疑很差, 处理数据可能十分耗时, 没人愿意傻等, 由于项目暂未使用ActiveMQ等消息队列中间件, 而redis的lpush和rpop很适合作为一种轻量级的消息队列实现, 所以用它完成此次功能开发 一.本文涉及知识点 excel文件读写--阿里easyexcel sdk 文件上传.下载--腾讯云对象存储 远程服务调用--restTe

  • SpringBoot通过RedisTemplate执行Lua脚本的方法步骤

    lua 脚本 Redis 中使用 lua 脚本,我们需要注意的是,从 Redis 2.6.0后才支持 lua 脚本的执行. 使用 lua 脚本的好处: 原子操作:lua脚本是作为一个整体执行的,所以中间不会被其他命令插入. 减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延. 复用性:lua脚本可以常驻在redis内存中,所以在使用的时候,可以直接拿来复用,也减少了代码量. 1.RedisScript 首先你得引入spring-boot-starter-data-redis依赖,其

  • Spring Boot单元测试中使用mockito框架mock掉整个RedisTemplate的示例

    概述 当我们使用单元测试来验证应用程序代码时,如果代码中需要访问Redis,那么为了保证单元测试不依赖Redis,需要将整个Redis mock掉.在Spring Boot中结合mockito很容易做到这一点,如下代码: import org.mockito.Mockito; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration

  • SpringBoot+Redis执行lua脚本的方法步骤

    1.背景 有时候,我们需要一次性操作多个 Redis 命令,但是 这样的多个操作不具备原子性,而且 Redis 的事务也不够强大,不支持事务的回滚,还无法实现命令之间的逻辑关系计算.所以,一般在开发中,我们会利用 lua 脚本来实现 Redis 的事务. 2.lua 脚本 Redis 中使用 lua 脚本,我们需要注意的是,从 Redis 2.6.0后才支持 lua 脚本的执行. 使用 lua 脚本的好处: 原子操作:lua脚本是作为一个整体执行的,所以中间不会被其他命令插入. 减少网络开销:可

  • SpringBoot通过redisTemplate调用lua脚本并打印调试信息到redis log(方法步骤详解)

    第一次写Lua脚本,并通过springboot的redisTemplate调用脚本,进行脚本与应用的交互.不熟悉真的折腾了好久,现在总结一下学习过程: 第一次写完lua时,想到的就是如何在应用调用脚本的时候,去调试脚本.在网上海搜了一把,能找到的有点相关的寥寥无几. 有一种方法是通过执行redis命令,调用redis客户端,加载lua脚本,然后出现基于命令行调试的交互界面,输入调试命令去调试脚本.如下: 在终端输入命令:redis-cli.exe --ldb --eval LimitLoadTi

  • springboot使用redisTemplate操作lua脚本

    目录 写在前面 使用lua 解释 写在前面 操作redis使用Lua脚本有诸多好处 减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数. 原子性的操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入.因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务. 代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑. 速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那

  • springboot中使用redis的方法代码详解

    特别说明: 本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​redis 作为一个高性能的内存数据库,如果不会用就太落伍了,之前在 node.js 中用过 redis,本篇记录如何将 redis 集成到 spring boot 中.提供 redis 操作类,和注解使用 redis 两种方式.主要内容如下: •docker 安装 redis •springboo

  • Redis调用Lua脚本及使用场景快速掌握

    目录 一.阅读本文前置条件 二.为什么需要Lua脚本 三.学点Lua语法 3.1.一个简单的例子 3.2.仔细看下Lua脚本里的内容 3.3.复杂点的例子 四.Lua脚本预加载 五.一个修改JSON数据的例子? 六.总结 一.阅读本文前置条件 可以遵循这个链接中的方法在操作系统上安装 Redis 如果你对redis命令不熟悉,查看<Redis 命令引用> 二.为什么需要Lua脚本 简而言之:Lua脚本带来性能的提升. 很多应用的服务任务包含多步redis操作以及使用多个redis命令,这时你可

  • 基于Redis+Lua脚本实现分布式限流组件封装的方法

    创建限流组件项目 pom.xml文件中引入相关依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springf

  • Java远程调用Shell脚本并获取输出信息【推荐】

    1.添加依赖 <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>262</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId&g

  • springboot config 拦截器使用方法实例详解

    本文介绍Spring-Boot中使用拦截器,一般在拦截器中处理跨域处理,允许跨域访问项目,拦截器使用详细资料请查阅官网. 实现自定义拦截器步骤: 1.创建一个类并实现HandlerInterceptor接口. 2.创建一个Java类继承WebMvcConfigurerAdapter,并重写 addInterceptors 方法. 2.将自定义的拦截器交由spring管理,然后将对像手动添加到拦截器链中(在addInterceptors方法中添加). 创建拦截器类 package com.exam

  • springboot 集成dubbo的步骤详解

    目录 第一步 搭建zookeeper环境 第二步 springboot集成dubbo 1.项目目录机构 2.代码编写 2.1 api目录 2.2 consumer目录 web访问.接口调用以及dubbo配置编写 2.3 provider目录 api接口实现以及dubbo配置 第三步 测试dubbo远程服务调用 附录: 写在前面:在阅读本文前,请前拥有dubbo基础知识,springboot知识 dubbo官网: http://dubbo.apache.orgdubbo github 源码地址:h

  • springboot中redis正确的使用详解

    redis实现了对数据的缓存,在项目里一些字典数据,会话数据,临时性数据都会向redis来存储,而在springboot里对redis也有支持,一般来说多个线程共同使用一个redis实现是有线程安全的风险的,而每个实现一个线程又太浪费资源,无法控制线程数量是非常危险的,所以就出现了一些redis线程池组件,下面说一下两个主要的组件. jedis 线程池主要是每个实例有自己的线程,线程可以从它建立的池子里获取lettuce lettuce是 apache推出的线程池工具,它的redis实例是可以被

随机推荐