You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redis Lua脚本调用print方法报错:全局变量print不存在

Redis Lua脚本报错:不存在全局变量'print'

错误现象

Docker环境下运行Redis,执行Lua脚本时触发如下错误:

Exception in thread "main" org.redisson.client.RedisException: ERR user_script:1: Script attempted to access nonexistent global variable 'print' script: 6f736423f082e141036b833d1f86b5a36a494611, on @user_script:1..

直接通过Redis CLI执行测试脚本,同样报错:

127.0.0.1:6379> eval "print("Comparison is_made b/w minimum_value out of two is: ")" 0
(error) ERR user_script:1: Script attempted to access nonexistent global variable 'print' script: 8598b7f0db450c711d3a9e73a296e331bd1ef945, on @user_script:1.

涉及的Java代码(基于Redisson库)

String script = "local rate = redis.call('hget', KEYS[1], 'rate');" + 
                "local interval = redis.call('hget', KEYS[1], 'interval');" + 
                "local type = redis.call('hget', KEYS[1], 'type');" + 
                "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')" + 
                "local valueName = KEYS[2];" + 
                "local permitsName = KEYS[4];" + 
                "if type == '1' then " + 
                "valueName = KEYS[3];" + 
                "permitsName = KEYS[5];" + 
                "end;" +
                "print(\"rate\"..rate) ;" +
                "print(\"interval\"..interval) ;" +
                "print(\"type\"..type); " + 
                "assert(tonumber(rate) >= tonumber(ARGV[1]), 'Requested permits amount could not exceed defined rate'); " + 
                "local currentValue = redis.call('get', valueName); " + 
                "local res;" + 
                "if currentValue ~= false then " + 
                "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); " + 
                "local released = 0; " + 
                "for i, v in ipairs(expiredValues) do " + 
                "local random, permits = struct.unpack('Bc0I', v);" + 
                "released = released + permits;" + 
                "end; " + 
                "if released > 0 then " + 
                "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); " + 
                "if tonumber(currentValue) + released > tonumber(rate) then " + 
                "currentValue = tonumber(rate) - redis.call('zcard', permitsName); " + 
                "else " + 
                "currentValue = tonumber(currentValue) + released; " + 
                "end; " + 
                "redis.call('set', valueName, currentValue);" + 
                "end;" + 
                "if tonumber(currentValue) < tonumber(ARGV[1]) then " + 
                "local firstValue = redis.call('zrange', permitsName, 0, 0, 'withscores'); " + 
                "res = 3 + interval - (tonumber(ARGV[2]) - tonumber(firstValue[2]));" + 
                "else " + 
                "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); " + 
                "redis.call('decrby', valueName, ARGV[1]); " + 
                "res = nil; " + 
                "end; " + 
                "else " + 
                "redis.call('set', valueName, rate); " + 
                "redis.call('zadd', permitsName, ARGV[2], struct.pack('Bc0I', string.len(ARGV[3]), ARGV[3], ARGV[1])); " + 
                "redis.call('decrby', valueName, ARGV[1]); " + 
                "res = nil; " + 
                "end;" + 
                "local ttl = redis.call('pttl', KEYS[1]); " + 
                "if ttl > 0 then " + 
                "redis.call('pexpire', valueName, ttl); " + 
                "redis.call('pexpire', permitsName, ttl); " + 
                "end; " + 
                "return res;";

RedissonClient client = null;
try {
    client = Redisson.create();
    client.getRateLimiter("user1:endpoint1").setRate(RateType.PER_CLIENT, 5, 1, RateIntervalUnit.SECONDS);
    String sha1 = client.getScript().scriptLoad(script);
    List<Object> keys = Arrays.asList("user1:endpoint1", "{user1:endpoint1}:value", "{user1:endpoint1}:value:febbb04d-6365-4cb8-b32b-8d90800cd4e6", "{user1:endpoint1}:permits", "{user1:endpoint1}:permits:febbb04d-6365-4cb8-b32b-8d90800cd4e6");
    byte[] random = new byte[8];
    ThreadLocalRandom.current().nextBytes(random);
    Object args[] = {1, System.currentTimeMillis(), random};
    boolean res = client.getScript().evalSha(READ_WRITE, sha1, RScript.ReturnType.BOOLEAN, keys, 1, System.currentTimeMillis(), random);
    System.out.println(res);
} finally {
    if(client != null && !client.isShutdown()){
        client.shutdown();
    }
}

问题补充

曾尝试改用io.write替代print,但依然触发相同错误。


解决方案

Redis内置的Lua运行环境没有对外暴露print、io.write这类输出全局函数——因为Redis的Lua脚本运行在隔离沙箱中,不允许直接向外部输出内容。

解决方式有两种:

  1. 移除调试代码:直接删除脚本中所有print(...)语句,生产环境的Lua脚本无需这类调试输出,这是最直接的处理方式。
  2. 通过返回值传递调试信息:如果需要调试变量值,可以将目标内容拼接成字符串,作为脚本返回值的一部分,在Java代码中接收后打印。例如修改脚本的返回逻辑:
    -- 在原return res前添加调试信息拼接
    local debugInfo = "rate:"..rate..",interval:"..interval..",type:"..type
    return {res, debugInfo}
    
    同时将Java代码中的返回类型改为RScript.ReturnType.MULTI,接收结果后即可打印调试信息。

内容的提问来源于stack exchange,提问作者Shiva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:01:18