【问题标题】:is it possible to call lua functions defined in other lua scripts in redis?是否可以在redis中调用其他lua脚本中定义的lua函数?
【发布时间】:2014-03-10 05:00:21
【问题描述】:

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

test = function ()    
    return 'test'
end



# from some other script
test()

编辑:

我不敢相信我仍然没有答案。我将包括我的设置的更多细节。

我正在使用带有 redis-scripto 包的 node 将脚本加载到 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

-- turns a dictionary table into a bulk reply table
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 })

引发以下错误。

[Error: ERR Error running script (call to f_d06f7fd783cc537d535ec59228a18f70fccde663): @enable_strict_lua:14: user_script:1: Script attempted to access unexisting global variable 'dict2bulk' ] undefined

【问题讨论】:

  • 我会问一个显而易见的问题:在第二个脚本中,您(或其他任何地方)是否“需要”指定函数的脚本?也就是说,这段代码真的可以运行吗?
  • 脚本通过 script load 命令加载到 redis 中。脚本本身没有任何要求。我需要它们吗?
  • 如果我使用 require,它会给我一个错误。脚本试图访问不存在的全局变量“require”。我不确定 require 是否可以在 Redis 中工作。
  • 另外,脚本运行。我可以运行正常执行的 redis 命令,但它对我在其他脚本中声明的函数一无所知。
  • @Nathan,你能告诉我在没有文件系统的情况下我应该如何在 Redis 中使用 require 吗?

标签: node.js lua redis


【解决方案1】:

我将与接受的答案相反,因为接受的答案是错误的。

虽然您无法显式定义命名函数,但您可以调用任何可以使用EVALSHA 调用的脚本。更具体地说,您通过SCRIPT LOAD 显式定义或通过EVAL 隐式定义的所有Lua 脚本都可以在f_<sha1 hash> 的全局Lua 命名空间中使用(直到/除非您调用SCRIPT FLUSH),您可以调用任何时间。

您遇到的问题是函数被定义为不带参数,而KEYSARGV 表实际上是全局变量。因此,如果您希望能够在 Lua 脚本之间进行通信,您要么需要修改 KEYSARGV 表,要么需要使用标准 Redis 键空间在函数之间进行通信。

127.0.0.1:6379> 脚本加载“返回 {KEYS[1], ARGV[1]}” “d006f1a90249474274c76f5be725b8f5804a346b” 127.0.0.1:6379> 评估“返回 f_d006f1a90249474274c76f5be725b8f5804a346b()”1“你好”“世界” 1)“你好” 2)“世界” 127.0.0.1:6379> eval "KEYS[1] = 'blah!'; return f_d006f1a90249474274c76f5be725b8f5804a346b()" 1 "hello" "world" 1)“废话!” 2)“世界” 127.0.0.1:6379>

所有这一切都说,这完全违反规范,如果您尝试在 Redis 集群场景中运行它,完全有可能以奇怪的方式停止工作。

【讨论】:

  • 嗨乔西亚,好消息。 完全违反规范让我有点担心,但我会考虑一下。我不知道这一点并不让我感到惊讶,因为它完全没有记录。好吧,当然除了 Redis 源代码本身。
  • 嗯,诀窍是规范并没有详细说明幕后实际发生的事情,只是一些你应该做和不应该做的事情。它没有说明函数是否可用、参数的存储位置等,尽管它自几年前使用脚本的 2.4 分支以来一直没有改变。
【解决方案2】:

重要提示:请参阅下面 Josiah 的回答。我的回答结果是错误或至少不完整。这当然让我很高兴,它让 Redis 更加灵活。

我的错误/不完整答案:

我很确定这是不可能的。不允许使用全局变量(阅读 docs ),并且脚本本身通过 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

【讨论】:

  • 我不太确定您所说的“但客户可以为下一步请求 SHA1”或“下一次通话”是什么意思。所以我听到的是你不能从脚本中运行脚本,但你可以在脚本之外使用 EVALSHA?这有什么帮助?
  • 它有助于运行集中测试的代码单元。这可能与你所追求的不同,我不确定。我确实希望我明确表示 Lua 脚本必须“独立”运行。这是设计使然(在我看来非常漂亮)。 Redis Lua 并不是一个代码库。
  • 好的,谢谢。我只是有一些用于格式化响应的方法,并希望在其他脚本中重用它们。看来我需要复制这些功能。
  • 不客气,这是正确的。从程序员的角度来看,它可能看起来并不“好”,但如果从分布式事务 NoSql 的角度来看,它就很有意义。而且它非常强大,你可以在网络上“奴役”你的逻辑。如果它适合您项目的范围/大小,我可以推荐针对此类问题的源代码控制自动化。在通用 NoSql 术语中:非规范化非常普遍。在这里你可以看到它也适用于源代码。
【解决方案3】:

因为我不是一个可以独自离开的人,所以我构建了一个允许简单内部调用语义的包。该软件包(适用于 Python)在 on GitHub 可用。

长话短说,它使用ARGV 作为调用堆栈,将KEYS/ARGV 引用转换为_KEYS_ARGV,使用Redis 作为名称-> 内部哈希映射,并转换@987654327 @ 到表追加 + Redis 查找 + Lua 函数调用。

METHOD.txt 文件描述了发生了什么,我用来翻译 Lua 脚本的所有正则表达式都可以在lua_call.py 中找到。随意重复使用我的语义。

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

【讨论】:

    猜你喜欢
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 1970-01-01
    • 2019-10-12
    • 2015-02-19
    • 2021-10-27
    相关资源
    最近更新 更多