在Redis中,是否可以调用其他Lua脚本中定义的Lua函数?

我试图在不使用 local 关键字声明函数的情况下,然后从另一个脚本调用该函数,但是当我运行该命令时,它会给我一个错误。

test = function ()
    return 'test'
end

#从其他脚本
test()

编辑:

我无法相信我还没有这个答案。我将包括更多关于我的设置的细节。

我正在使用 node 和 redis-scripto 包将脚本加载到 redis 中。这是一个例子。

var Scripto = require('redis-scripto');
var scriptManager = new Scripto(redis);

scriptManager.loadFromDir('./lua_scripts');

var keys    = [key1, key2];
var values  = [val];

scriptManager.run('run_function', keys, values, function(err, result) {
console.log(err, result)
})

和 lua 脚本。

-- ./lua_scripts/dict_2_bulk.lua

-- 将字典表转换为批量回复表
dict2bulk = function (dict)
    local result = {}
    for k, v in pairs(dict) do
        table.insert(result, k)
        table.insert(result, v)
    end
    return result
end

-- run_function.lua

return dict2bulk({test = 1})

抛出以下错误。

[错误:ERR运行脚本错误(调用f_d06f7fd783cc537d535ec59228a18f70fccde663):@ enable_strict_lua:14:user_script:1:脚本尝试访问不存在的全局变量'dict2bulk']未定义
点赞
用户2759336
用户2759336

重要通知: 请参考Josiah的下面的回答。我的回答证明是 错误 或至少是 不完整 的。 当然这使我非常高兴,这使得Redis更加灵活。

我的不正确/不完整的回答:

我很确定这是不可能的。 您不允许使用全局变量(阅读文档),并且由Redis Lua引擎生成的脚本本身具有本地和临时作用域。

Lua函数自动在幕后设置“写入”标志,如果它们执行任何写操作,则会启动事务。 如果您级联Lua调用,则Redis中的簿记将变得非常麻烦,特别是当级联在Redis从服务器上执行时。 这就是为什么 EVALEVALSHA 故意未在Lua脚本内作为有效的Redis调用出现的原因。调用您正在尝试执行的已“加载”的Lua函数也是一样的。 如果在第一个脚本的加载和第二个脚本的执行之间重启从服务器会发生什么?

我们如何克服这种限制:

不要使用 EVAL,只使用 SCRIPT LOADEVALSHA。 将SHA1存储在redis哈希集中。

我们在我们的版本控制系统中自动化了这个过程,因此已提交的Lua脚本自动在Redis主服务器中以哈希集的形式存储其SHA1校验和并赋予逻辑名称。客户端不能使用EVAL(在从服务器上;我们在配置中禁用了EVAL+LOAD)。但是,客户端可以请求下一步的SHA1。 几乎所有我们的Lua函数都返回下一步的SHA1。

希望这有所帮助,TW。

2014-02-20 23:08:43
用户490681
用户490681

我要反对被接受的回答,因为被接受的回答是错误的。

尽管您不能明确定义命名函数,但是您可以调用任何可用EVALSHA调用的脚本。更具体地说,您通过SCRIPT LOAD或隐式EVAL明确定义的所有Lua脚本都在全局Lua命名空间中可用于f_<sha1 hash>(直到/除非您调用SCRIPT FLUSH),您可以随时调用它们。

你遇到的问题是函数被定义为不接受参数,而KEYSARGV表实际上是全局的。因此,如果您希望在Lua脚本之间进行通信,您需要操纵KEYSARGV表,或者您需要使用标准Redis keyspace在函数之间进行通信。

127.0.0.1:6379> script load "return {KEYS[1], ARGV[1]}"
"d006f1a90249474274c76f5be725b8f5804a346b"
127.0.0.1:6379> eval "return f_d006f1a90249474274c76f5be725b8f5804a346b()" 1 "hello" "world"
1) "hello"
2) "world"
127.0.0.1:6379> eval "KEYS[1] = 'blah!'; return f_d006f1a90249474274c76f5be725b8f5804a346b()" 1 "hello" "world"
1) "blah!"
2) "world"
127.0.0.1:6379>

所有这些说法都违反了规范,并且在尝试在Redis集群场景中运行此操作时可能以奇怪的方式停止工作。

2014-03-24 02:00:58
用户490681
用户490681

因为我不会轻易放弃,所以我开发了一个包,可以实现简单的内部调用语义。这个(适用于 Python 的)包可以在 GitHub 上 找到。

长话短说,它使用 ARGV 作为调用堆栈,将 KEYS/ ARGV 的引用转换为 _KEYS_ARGV,在内部使用 Redis 作为名称->哈希映射,并将 CALL.<name>(<keys>, <argv>) 转换为一个表追加 + Redis 查找 + Lua 函数调用。

METHOD.txt 文件描述了发生的事情,我用来转换 Lua 脚本的所有正则表达式都可以在 lua_call.py 中找到。欢迎使用我的语义。

函数注册表的使用使这在 Redis 群集或任何其他多分片设置中非常不可能工作,但对于单主应用程序,它应该在可预见的未来内可行。

2014-03-30 23:32:44