纯lua 问题: attempt to ... (a nil value)

1,458 views
Skip to first unread message

Zoom.Quiet

unread,
Mar 7, 2012, 11:22:00 AM3/7/12
to open...@googlegroups.com
刚刚开始写 lua 就遇到非常超出经验的问题:
一个原型函式:
function checkForValidUrl(uri)
return uri
end

调用:
local url = "sina.cn"
checkForValidUrl(url)

就一直报:
*150 lua handler aborted: runtime error:
/usr/local/openresty/nginx/conf/lua/chk.lua:30:
attempt to call global 'checkForValidUrl' (a nil value),...

这算什么事儿吼,,,

--
人生苦短, Pythonic! 冗余不做,日子甭过!备份不做,十恶不赦!
俺: http://about.me/zoom.quiet
文字协议: http://creativecommons.org/licenses/by-sa/2.5/cn/

Zoom.Quiet

unread,
Mar 7, 2012, 11:43:30 AM3/7/12
to open...@googlegroups.com
在 2012年3月8日 上午12:22,Zoom.Quiet <zoom....@gmail.com> 写道:
> 刚刚开始写 lua 就遇到非常超出经验的问题:
> 一个原型函式:
> function checkForValidUrl(uri)
>    return uri
> end
>
> 调用:
>    local url = "sina.cn"
>    checkForValidUrl(url)
>
> 就一直报:
> *150 lua handler aborted: runtime error:
> /usr/local/openresty/nginx/conf/lua/chk.lua:30:
>  attempt to call global 'checkForValidUrl' (a nil value),...
>
> 这算什么事儿吼,,,
>
- FT! 超低级錯誤!
- 俺将函式定义,放在了调用语句的后面,,,

discuss is closed ;-(

lhmwzy

unread,
Mar 7, 2012, 6:26:36 PM3/7/12
to open...@googlegroups.com
嗯,我也犯过同样的错误,现在学精了,都先定义函数,再在后面使用函数。

> --
> 邮件自: 列表"openresty",专用于技术讨论!
> 发言: 请发邮件到 open...@googlegroups.com
> 退订: 请发邮件至 openresty+...@googlegroups.com
> 详情: http://groups.google.com/group/openresty
> 官网: http://openresty.org/
> 仓库: https://github.com/agentzh/ngx_openresty
> 建议: 提问的智慧 http://wiki.woodpecker.org.cn/moin/AskForHelp

agentzh

unread,
Mar 7, 2012, 10:00:45 PM3/7/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 12:22 AM, Zoom.Quiet <zoom....@gmail.com> wrote:
刚刚开始写 lua 就遇到非常超出经验的问题:
一个原型函式:
function checkForValidUrl(uri)
   return uri
end

调用:
   local url = "sina.cn"
   checkForValidUrl(url)


Lua 里的 function 定义本质上是变量赋值,即

    function foo() ... end

等价于

    foo = function () ... end

因此在函数定义之前使用函数相当于在变量赋值之前使用变量,自然会得到 nil 的错误。

一般地,由于全局变量是每请求的生命期,因此以此种方式定义的函数的生命期也是每请求的。为了避免每请求创建和销毁 Lua closure 的开销,建议将函数的定义都放置在自己的 Lua module 中,例如:

    -- my_module.lua
    module("my_module", package.seeall)
    function foo() ... end

然后,再在 content_by_lua_file 指向的 .lua 文件中调用它:

    local my_module = require "my_module"
    my_module:foo()

因为 Lua module 只会在第一次请求时加载一次(除非显式禁用了 lua_code_cache 配置指令),后续请求便可直接复用。

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 8, 2012, 6:08:07 AM3/8/12
to open...@googlegroups.com
在 2012年3月8日 上午11:00,agentzh <age...@gmail.com> 写道:
> On Thu, Mar 8, 2012 at 12:22 AM, Zoom.Quiet <zoom....@gmail.com> wrote:
>>
>> 刚刚开始写 lua 就遇到非常超出经验的问题:
>> 一个原型函式:
>> function checkForValidUrl(uri)
>>    return uri
>> end
>>
>> 调用:
>>    local url = "sina.cn"
>>    checkForValidUrl(url)
>>
>
> Lua 里的 function 定义本质上是变量赋值,即
>
>     function foo() ... end
>
> 等价于
>
>     foo = function () ... end
>
> 因此在函数定义之前使用函数相当于在变量赋值之前使用变量,自然会得到 nil 的错误。
>
> 一般地,由于全局变量是每请求的生命期,因此以此种方式定义的函数的生命期也是每请求的。为了避免每请求创建和销毁 Lua closure
> 的开销,建议将函数的定义都放置在自己的 Lua module 中,例如:
>
>     -- my_module.lua
>     module("my_module", package.seeall)
>     function foo() ... end
>
> 然后,再在 content_by_lua_file 指向的 .lua 文件中调用它:
>
>     local my_module = require "my_module"
>     my_module:foo()
>

非常自然的方式,不过,俺这样作,error":
[error] 3749#0: *10 lua handler aborted: runtime error:
/usr/local/openresty/nginx/conf/lua/chk.lua:3: m
odule 'kcs' not found:
no field package.preload['kcs']
no file '/usr/local/openresty/lualib/kcs.lua'
no file './kcs.lua'
no file '/usr/local/openresty/luajit/share/luajit-2.0.0-beta9/kcs.lua'
no file '/usr/local/share/lua/5.1/kcs.lua'
no file '/usr/local/share/lua/5.1/kcs/init.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/kcs.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/kcs/init.lua'
no file '/usr/local/openresty/lualib/kcs.so'
,,,

即使俺在nginx 配置中有:
# 设置纯Lua扩展库PATH(';;' is the default path):
lua_package_path 'conf/lua/?.lua;;';
# or
lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';

都无法在 chk.lua 中通过:
local KCS = require "kcs"

加载: /usr/local/openresty/nginx/conf/lua/kcs.lua
-- KCS API support
module("kcs", package.seeall)
...

怎么想屔,,,

> 因为 Lua module 只会在第一次请求时加载一次(除非显式禁用了 lua_code_cache 配置指令),后续请求便可直接复用。
>
> Regards,
> -agentzh

--

Simon

unread,
Mar 8, 2012, 8:26:48 AM3/8/12
to openresty
我碰到过很奇怪的问题,也是require("abc"), 但是在abc.lua里头没有使用 module("abc",
package.seeall)

然后在外头调用 abc.xxx,

第一次curl成功了,后面curl就500,报nil function;

然后reload,又是第一次ok,后面失败。

求解释。

Zoom.Quiet

unread,
Mar 8, 2012, 8:38:25 AM3/8/12
to open...@googlegroups.com
嗯嗯嗯,福灵心致,无意中解决,,,
参考:lua-users wiki: Modules Tutorial
http://lua-users.org/wiki/ModulesTutorial
俺的布局:
/usr/local/openresty/nginx/conf
+- lua
+- ksc.lua
+- chk.lua
-- ksc.lua
module(..., package.seeall)
,,,
-- chk.lua
local KSC = require "lua.ksc"

就一切安定了,agentzh 没说:
- module(..., package.seeall) 这儿直接就用 ...
- 所有 openresty 环境中,俺猜都是从 nginx/conf 为起点的
- 所以,即使 包和应用都在同一级目录中
- 依然要使用子包的模式来引用 ;-(

lhmwzy

unread,
Mar 8, 2012, 8:43:04 AM3/8/12
to open...@googlegroups.com
介个。。。看看lua-resty-*系列的库,就明白如何使用自己写的库了

Zoom.Quiet

unread,
Mar 8, 2012, 8:57:39 AM3/8/12
to open...@googlegroups.com
在 2012年3月8日 下午9:43,lhmwzy <lhm...@gmail.com> 写道:
> 介个。。。看看lua-resty-*系列的库,就明白如何使用自己写的库了
>
嗯嗯嗯?! mongo 的还没有完成?
https://github.com/ericzhang-cn/lua-resty-mongo
lib/
+- bson.lua
+- resty/mongo.lua
-- bson.lua
module("bson", package.seeall)

-- resty/mongo.lua
module("resty.mongo", package.seeall)
local bit = require "bit"
local bson = require "bson"

这怎么回事儿吼?
- 下层调用上层的不用 ../ 的?
- 反正这方面,俺一直 chaos ,蒙对就一直用,没有深入 ;-)

agentzh

unread,
Mar 8, 2012, 9:13:36 AM3/8/12
to open...@googlegroups.com
应该是因为你并没有这样 require 你的模块:

   local abc = require "abc"

而是(不正确地)直接写成

   require "abc"

在 ngx_lua 中,全局变量的生命期都是每请求的,所以这里不能依赖 require 函数自动在第一次加载模块时自动设置与模块同名的全局变量的行为。

具体解释在官方文档中有:

http://wiki.nginx.org/HttpLuaModule#Lua_Variable_Scope

Regards,
-agentzh

agentzh

unread,
Mar 8, 2012, 9:19:17 AM3/8/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 7:08 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
非常自然的方式,不过,俺这样作,error":
[error] 3749#0: *10 lua handler aborted: runtime error:
/usr/local/openresty/nginx/conf/lua/chk.lua:3: m
odule 'kcs' not found:
       no field package.preload['kcs']
       no file '/usr/local/openresty/lualib/kcs.lua'
       no file './kcs.lua'
       no file '/usr/local/openresty/luajit/share/luajit-2.0.0-beta9/kcs.lua'
       no file '/usr/local/share/lua/5.1/kcs.lua'
       no file '/usr/local/share/lua/5.1/kcs/init.lua'
       no file '/usr/local/openresty/luajit/share/lua/5.1/kcs.lua'
       no file '/usr/local/openresty/luajit/share/lua/5.1/kcs/init.lua'
       no file '/usr/local/openresty/lualib/kcs.so'
,,,


这里的出错信息已经详细地指出了 Lua 尝试过的加载路径。如果你的 kcs.lua 在 /foo/bar/blah/ 下,则应当在 lua_package_path 配置指令中添加 /foo/bar/blah/?.lua 这一项。另外,使用相对路径时要小心,你认为的当前工作目录可能并不是实际的当前工作目录。

Regards,
-agentzh

agentzh

unread,
Mar 8, 2012, 9:20:56 AM3/8/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 9:57 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
嗯嗯嗯?! mongo 的还没有完成?
https://github.com/ericzhang-cn/lua-resty-mongo
lib/
   +- bson.lua
   +- resty/mongo.lua
-- bson.lua
module("bson", package.seeall)

--  resty/mongo.lua
module("resty.mongo", package.seeall)
local bit = require "bit"
local bson = require "bson"

这怎么回事儿吼?
- 下层调用上层的不用 ../ 的?
- 反正这方面,俺一直 chaos ,蒙对就一直用,没有深入 ;-)


我建议你仔细阅读 Lua 官方手册或者图书。你这里混淆了 Lua 模块名和库搜索路径的概念。二者之间有映射关系,但不是一回事。

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 8, 2012, 9:32:59 AM3/8/12
to open...@googlegroups.com
在 2012年3月8日 下午10:19,agentzh <age...@gmail.com> 写道:
>> 非常自然的方式,不过,俺这样作,error":
>> [error] 3749#0: *10 lua handler aborted: runtime error:
>> /usr/local/openresty/nginx/conf/lua/chk.lua:3: m
>> odule 'kcs' not found:
>>        no field package.preload['kcs']
>>        no file '/usr/local/openresty/lualib/kcs.lua'
>>        no file './kcs.lua'
- 我的 chk.lua 以及 kcs.lua 在同一目录中,
- 这里的 ./kcs.lua 为什么无法获取?

>>        no file
>> '/usr/local/openresty/luajit/share/luajit-2.0.0-beta9/kcs.lua'
>>        no file '/usr/local/share/lua/5.1/kcs.lua'
>>        no file '/usr/local/share/lua/5.1/kcs/init.lua'
>>        no file '/usr/local/openresty/luajit/share/lua/5.1/kcs.lua'
>>        no file '/usr/local/openresty/luajit/share/lua/5.1/kcs/init.lua'
>>        no file '/usr/local/openresty/lualib/kcs.so'
>> ,,,
>>
>
> 这里的出错信息已经详细地指出了 Lua 尝试过的加载路径。如果你的 kcs.lua 在 /foo/bar/blah/ 下,则应当在
> lua_package_path 配置指令中添加 /foo/bar/blah/?.lua
> 这一项。另外,使用相对路径时要小心,你认为的当前工作目录可能并不是实际的当前工作目录。
>

- 收到!
- 不过, lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
俺的确加了
server{
这里尝试过
...
location ~ ^/=/(\w+) {
这里也尝试过
...
}
}

好象 lua_package_path 没有执行一样,
openresty 是否有 pachage_path 的查询功能?
- 可以看到当前请求时的包查询路径列表?

agentzh

unread,
Mar 8, 2012, 9:42:35 AM3/8/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 10:32 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
- 收到!
- 不过, lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
       俺的确加了
server{
   这里尝试过

如果你添加到 lua_package_path 指令中的搜索路径并没有出现在 Lua require 函数抛出的出错信息列出的尝试过的加载路径的清单中,则可以检查一下是否忘记重启 Nginx 或者 Nginx 是否重启失败(后者可以通过检查 error.log 来确认) :)

另外,值得一提的是,lua_package_path 指令并不能在 server 配置块中使用,如果你用了会在 error.log 或者终端上得到下面这条错误:

[emerg] "lua_package_path" directive is not allowed here

Best regards,
-agentzh

agentzh

unread,
Mar 8, 2012, 9:44:52 AM3/8/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 10:32 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
openresty 是否有 pachage_path 的查询功能?
         - 可以看到当前请求时的包查询路径列表?


我刚才已经在回复中指出过了,Lua require 函数抛出的出错信息中就列举了实际查找过的路径,比如:


[error] 3749#0: *10 lua handler aborted: runtime error:
/usr/local/openresty/nginx/
conf/lua/chk.lua:3: m
odule 'kcs' not found:
       no field package.preload['kcs']
       no file '/usr/local/openresty/lualib/kcs.lua'
       no file './kcs.lua'
       no file '/usr/local/openresty/luajit/share/luajit-2.0.0-beta9/kcs.lua'
       no file '/usr/local/share/lua/5.1/kcs.lua'
       no file '/usr/local/share/lua/5.1/kcs/init.lua'
       no file '/usr/local/openresty/luajit/share/lua/5.1/kcs.lua'
       no file '/usr/local/openresty/luajit/share/lua/5.1/kcs/init.lua'
       no file '/usr/local/openresty/lualib/kcs.so'

这还不够清楚么?呵呵。

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 8, 2012, 9:55:37 AM3/8/12
to open...@googlegroups.com

- FT! 俺一直使用 reload 来重载 nginx 的
- 使用 restart 后,才真正检查配置,才见到这一提示,,,

了了,,,

- 维基文档中除了示范中暗示了命令应该的位置,其它没有任何地方进行强调
- 看来任何一份儿手册,都隐藏了无数细节,有侍撞上,,,

Zoom.Quiet

unread,
Mar 8, 2012, 9:58:24 AM3/8/12
to open...@googlegroups.com

- 嗯嗯嗯,这是 error 的输出,給人看的,
- 俺在想,openresty 是否有相关的自省能力
- 可以在运行时,自我检验 内置路径,进行加载的修补
- 不过,现在想来,Nginx 的完整世界是一但跑起来就自成一国的
- 不能随意变更的
- 居然有热部属的能力,但是,这不应该是 openresty 想的事儿?

agentzh

unread,
Mar 9, 2012, 9:35:39 AM3/9/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 10:58 PM, Zoom.Quiet <zoom....@gmail.com> wrote:

- 嗯嗯嗯,这是 error 的输出,給人看的,
- 俺在想,openresty 是否有相关的自省能力
 - 可以在运行时,自我检验 内置路径,进行加载的修补
 - 不过,现在想来,Nginx 的完整世界是一但跑起来就自成一国的
 - 不能随意变更的
 - 居然有热部属的能力,但是,这不应该是 openresty 想的事儿?


如果你的意思是 lua_package_path 指令有 bug,请给出一个最小化的完整示例以方便我们复现问题。我不明白库的搜索路径这么简单的事情和“自我检验”和“加载修补”这些东西有何关系 :)

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 9, 2012, 10:36:59 AM3/9/12
to open...@googlegroups.com

是也乎,俺没有説明白
- 不是问题,是期望的新功能了,,,
- 具体场景,俺有合适示例时,再提出吧,

thanx for all

agentzh

unread,
Mar 10, 2012, 3:31:13 AM3/10/12
to open...@googlegroups.com
On Thu, Mar 8, 2012 at 9:38 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
> 嗯嗯嗯,福灵心致,无意中解决,,,
> 参考:lua-users wiki: Modules Tutorial
> http://lua-users.org/wiki/ModulesTutorial
> 俺的布局:
> /usr/local/openresty/nginx/conf
>    +- lua
>       +- ksc.lua
>       +- chk.lua
> -- ksc.lua
> module(..., package.seeall)
> ,,,
> -- chk.lua
> local KSC = require "lua.ksc"
>
> 就一切安定了,agentzh 没说:
> - module(..., package.seeall) 这儿直接就用 ...
> - 所有 openresty 环境中,俺猜都是从 nginx/conf 为起点的
>  - 所以,即使 包和应用都在同一级目录中
>  - 依然要使用子包的模式来引用 ;-(
>

目前,在 ngx_lua 中,如果在 lua_package_path 或者 lua_package_cpath 配置指令中指定相对路径作为 Lua 包的搜索路径,其实是相对于启动 nginx 服务器时的当前工作目录的,而不是 nginx 的“配置前缀”(configure prefix)。

举例来说,假设你是在 /home/agentzh/ 这个当前工作目录下用下面的命令启动 nginx:

   /usr/local/openresty/nginx/sbin/nginx

则 nginx 内部包括 Lua 环境所使用的当前工作目录就是执行启动命令时的当前工作目录,也就是 /home/agentzh/,而不是许多人期望的 Nginx “配置前缀”(在该例中便是 /usr/local/openresty/nginx/sbin/nginx/)。

通过下面的接口可以动态获取实际使用的当前工作目录:

   location = /pwd {
       content_by_lua '
           local fname = "/tmp/pwd.out"
           assert(os.execute("pwd > " .. fname) == 0)
           local f = io.open(fname, "r")
           assert(f)
           local content = f:read("*a")
           ngx.say("pwd: ", content)
           f:close()
       ';
   }

对于上面那个例子,访问 /pwd 可以得到输出

   pwd: /home/agentzh

这个确实有些违反直觉。毕竟在 nginx 中的许多地方,相对路径都是相对于“配置前缀”路径的,而非真实的当前工作目录。

或许我们应该在 nginx 启动时自动把整个进程所使用的当前工作目录切换到 nginx 的“配置前缀”路径?大家伙有什么看法?如果真要做这件事情的话,因为在 ngx_lua 中实现,还是以单独的 nginx 模块的形式实现,还是以 nginx 核心的补丁的形式来实现?

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 10, 2012, 4:09:01 AM3/10/12
to open...@googlegroups.com
- 神奇! 即使俺是通过 /opt/sbin/openresty.server 伺候脚本重启 nginx 的
- 也一样记录为调用 openresty.server 时的所在目录!!

> 或许我们应该在 nginx 启动时自动把整个进程所使用的当前工作目录切换到 nginx
> 的“配置前缀”路径?大家伙有什么看法?如果真要做这件事情的话,因为在 ngx_lua 中实现,还是以单独的 nginx 模块的形式实现,还是以
> nginx 核心的补丁的形式来实现?
>

- 如果这是 nginx 的内核行为,建议不进行补丁
- 而是提供专用模块,进行显式声明,形如
http{
ngx_workroot_as_path '/usr/local/openresty/nginx/';
# 配合起来就可配置文件全局使用 相对路径了!
lua_package_path 'conf/lua/?.lua;;';
}
- 进一步的,允许多次配置 ngx_workroot_as_path
- 使不同功能的 ngx_lua 业务模块,可以分發在不同的起始目录?
- 以便进行合理的分仓库版本/发布/升级管理


> Regards,
> -agentzh

agentzh

unread,
Mar 10, 2012, 4:59:41 AM3/10/12
to open...@googlegroups.com
On Sat, Mar 10, 2012 at 5:09 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
- 神奇! 即使俺是通过 /opt/sbin/openresty.server 伺候脚本重启 nginx 的
- 也一样记录为调用 openresty.server 时的所在目录!!


其实最简单的方法是在你的启动脚本中先自己 cd 到 nginx 的配置前缀目录,比如 /usr/local/openresty/nginx/,然后再行启动 nginx.

Best,
-agentzh

Zoom.Quiet

unread,
Mar 10, 2012, 5:03:36 AM3/10/12
to open...@googlegroups.com

- 嗯嗯嗯,最土的,就是最有效的方式!
- 收到! 收录到乱入手册中 ;-)

> Best,

Reply all
Reply to author
Forward
0 new messages