需要注意的点
时间 API
返回当前时间的 API,如果没有非阻塞网络 IO 操作来触发,便会一直返回缓存的值,而不是像我们想的那样,能够返回当前的实时时间。
1 | $ resty -e 'ngx.say(ngx.now()) |
在两次调用 ngx.now 之间,使用 Lua 的阻塞函数 sleep 了 1 秒钟,但从打印的结果来看,这两次返回的时间戳却是一模一样的。
如果换成是非阻塞的 sleep 函数,它就会打印出不同的时间戳了。
1 | $ resty -e 'ngx.say(ngx.now()) |
json 编码时无法区分 array 和 dict
json 对空 table 编码的时候,无法确定编码为数组还是字典。
OpenResty 的 cjson 库,默认把空 table 当做字典来编码。我们可以通过 encode_empty_table_as_object 这个函数,来修改这个全局的默认值:
1 | resty -e 'local cjson = require "cjson" |
全局这种设置的影响面比较大,可以指定某个 table 的编码规则:
1 | resty -e 'local cjson = require "cjson" |
真值和空值
ngx.null
因为 Lua 的 nil 无法作为 table 的 value,所以 OpenResty 引入了 ngx.null,作为 table 中的空值。
只有 nil 和 false 是假值,ngx.null 的布尔值为真。如果遗漏了这一点,就很容易踩坑,比如在使用 lua-resty-redis 的时候,做了下面这个判断:
1 | local res, err = red:get("dog") |
在 dog 这个 key 不存在的情况下,这段代码就 500 崩溃了。
cdata:NULL
当通过 LuaJIT FFI 接口去调用 C 函数,而这个函数返回一个 NULL 指针,那么就会遇到另外一种空值,即 cdata:NULL 。
和 ngx.null 一样,cdata:NULL 也是真值。但下面这段代码,会打印出 true,也就是说 cdata:NULL 是和 nil 相等的:
1 | $ resty -e 'local ffi = require "ffi" |
cjson.null
Lua 中的 nil,被 json encode 和 decode 一圈儿之后,就变成了 cjson.null。它引入的原因和 ngx.null 是一样的,因为 nil 无法在 table 中作为 value。
1 | $ resty -e 'local cjson = require "cjson" |
变量的个数限制
Lua 中,一个函数的局部变量的个数,和 upvalue 的个数都是有上限的,你可以从 Lua 的源码中得到印证:
1 | /* |
这两个阈值,分别被硬编码为 200 和 60。虽说你可以手动修改源码来调整这两个值,不过最大也只能设置为 250。
一般情况下,我们不会超过这个阈值,但写 OpenResty 代码的时候,还是要留意这个事情,不要过多地使用局部变量和 upvalue,而是要尽可能地使用 do .. end 做一层封装,来减少局部变量和 upvalue 的个数。
1 | local re_find = ngx.re.find |
如果只有函数 foo 使用到了 re_find, 可以这样改造下:
1 | do |
这样一来,在 main 函数的层面上,就少了 re_find 这个局部变量。这在单个的大的 Lua 文件中,算是一个优化技巧。