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脚本运行在隔离沙箱中,不允许直接向外部输出内容。
解决方式有两种:
- 移除调试代码:直接删除脚本中所有
print(...)语句,生产环境的Lua脚本无需这类调试输出,这是最直接的处理方式。 - 通过返回值传递调试信息:如果需要调试变量值,可以将目标内容拼接成字符串,作为脚本返回值的一部分,在Java代码中接收后打印。例如修改脚本的返回逻辑:
同时将Java代码中的返回类型改为-- 在原return res前添加调试信息拼接 local debugInfo = "rate:"..rate..",interval:"..interval..",type:"..type return {res, debugInfo}RScript.ReturnType.MULTI,接收结果后即可打印调试信息。
内容的提问来源于stack exchange,提问作者Shiva
相关产品推荐
相关产品推荐

