使用 RedisTemplate 执行 Lua 脚本永远不起作用

Posted

技术标签:

【中文标题】使用 RedisTemplate 执行 Lua 脚本永远不起作用【英文标题】:Executing Lua Script with RedisTemplate never works 【发布时间】:2020-11-04 05:47:57 【问题描述】:

我目前正在为 kafka 和 GC PubSub 开发基准工具。我想查看关于 min 的结果。最大和平均传输速度。在之前,我将每个条目设置为 reddis 哈希。之后,我将 reddis 哈希映射到 java map 并在进程结束时对它们进行迭代以获取最小值、最大值。它看起来很慢,因为如果我调用 100000 条目,程序会迭代 x3 的最小值、最大值和平均值。所以我尝试使用 Lua 脚本来做到这一点。发布消息后,我设置了哈希映射的开始时间,当侦听器收到消息时,我从哈希中获取消息的开始时间,并计算与System.currentTimeMillis 的差异。在这一步之后,我尝试执行 Lua 脚本,将当前值与旧值进行比较并设置它。但是当我执行脚本时,程序似乎停在那里。我尝试从 Lua 脚本返回 true,但没有得到任何响应。

 private void calculateSetANDLogAgain(User user)
        long startTime = ((long) redisTemplate.opsForHash().get("times", user.getId()));
        logger.info("Received message -> " + user.toString());
        long duration = 0L;
        duration = System.currentTimeMillis() - startTime;
        Object[] args = new Object[1];
        args[0] = duration;
        System.out.println("BEFORE");
        boolean a = redisTemplate.execute(statisticScript, Collections.singletonList("a"),args);
        System.out.println("AFTER: " + a);
    

在这里我看到之前但我看不到打印后。这是关于脚本执行的log 输出和配置。这是我的class tree。请注意,RedisConfig 类具有以下 bean。

 @Bean
    public DefaultRedisScript<Boolean> redisscript()
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<>();
        defaultRedisScript.setLocation(new ClassPathResource("statistics.lua"));
        defaultRedisScript.setResultType(Boolean.class);
        return defaultRedisScript;
    

我在拥有calculateSetANDLogAgain 方法的类中自动装配DefaultRedisScript 实例。脚本文件只有“return true;

编辑;如果有帮助,这是我的第一个脚本。

local difference = tonumber(ARGV[1])
local max = tonumber(redis.call("GET","max"))
local min = tonumber(redis.call("GET","min"))
if max == nil then
    redis.call("SET","max",difference)
elseif difference > max then
    redis.call("SET","max",difference)
end
if min == nil then
    redis.call("SET","min",difference)
elseif difference < min then
    redis.call("SET","min",difference)
end

那么可能出了什么问题?我想不通...

【问题讨论】:

【参考方案1】:

我解决了这个问题。这很奇怪,但似乎失败的根源是错误的类路径。我对代码进行了一些更改,请看一下。

这是Bean 的定义。

   @Bean
    public DefaultRedisScript<Boolean> redisscript() 
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<Boolean>();
        defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("statistics.lua")));
        defaultRedisScript.setResultType(Boolean.class);
        return defaultRedisScript;
    

这是calculateSetANDLogAgain 函数。

    private void calculateSetANDLogAgain(User user) 
        long startTime = ((long) redisTemplate.opsForHash().get("times", user.getId()));
        logger.info("Received message -> " + user.toString());
        Long duration = null;
        duration = System.currentTimeMillis() - startTime;
        Object[] args = new Object[1];
        args[0] = duration;
        try 
            redisTemplate.execute(statisticScript, null, args);
         catch (Exception e) 
            finalLogger.info("Error while executing script -> " + e.getLocalizedMessage());
        
    

这是脚本;

local difference = tonumber(ARGV[1])
local max = tonumber(redis.call("GET","max"))
local min = tonumber(redis.call("GET","min"))
if max == nil then
    redis.call("SET","max",tostring(difference));
elseif difference > max then
    redis.call("SET","max",tostring(difference));
end

if min == nil then
    redis.call("SET","min",tostring(difference));
elseif difference < min then
    redis.call("SET","min",tostring(difference));
end
return nil;

最后我的脚本在资源文件夹中,我在构造函数中注入了 redistemplate。

【讨论】:

以上是关于使用 RedisTemplate 执行 Lua 脚本永远不起作用的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot通过RedisTemplate执行Lua脚本

springboot redis 用RedisTemplate执行lua脚本报错:@user_script:1: ERR value is not an integer or out of range

使用RedisTemplate+Lua脚本实现Redis分布式锁

redis的lua脚本快速入门

redis原子性读写操作

lua 变量