摘自:http://www.daileinote.com/computer/openresty/04

局部模块在同一个worker中是共享的

在同一个 worker 中,使用 local 修饰的模块代码和数据只会被加载一次,该模块下的数据在同一个 worker 下的所有请求共享,但是全局的却不是(也就是不加 local 修饰),那是由于一个请求一个协程的设计原则。

1
2
3
4
5
6
location /lua {
content_by_lua_block {
local mydata = require "mydata"
ngx.say(mydata.get_age("dog"))
}
}

当第一次请求 /lua 时,当前 worker 会加载缓存该模块,接下来所有到 /lua 的请求都会使用预先加载的数据,这也是 openresty 高性能的原因所在。

一定要注意,数据在同一个 worker 下共享,换句话说,启动 nginx 如果有多个 worker 进程,它们之间是不共享数据的,如果想多个 worker 进程之间共享数据,可以利用如下计数:

1.使用 ngx.shared.DICT api(接下来会讲到)
2.使用第三方的如 memcached、redis、mysql等

变量作用域

在大多数情况下,变量声明的时候带上 local 标识符,声明为局部变量。

原因是,在设计上,全局变量是跟请求相关联的,也就是说一个请求一个全局环境。如果一个变量声明为全局,那么它的生命周期会跟随一个请求的建立到一个请求的消亡,当一个请求结束时,该变量会被清除,而且在全局环境中搜寻开销比在局部里大。静止使用全局变量的原因如下:

1.在高并发的情况下可能会有意想不到情况发生
2.在全局环境里搜索变量开销很大,影响性能
3.不易于调试

openresty 作者如此强调,所以就不用多想,所有变量都声明为局部即可,比如

1
2
3
4
5
6
7
8
9
-- 避免
foo = 123
-- 推荐
local foo = 123

-- 避免
function foo() return 123 end
-- 推荐
local function foo() return 123 end

但是有时候,我们开发一个大的项目,总有大意的时候,如果不小心把变量声明为全局了,我们也很难发现。

作者早就帮我们想到了,我们可以使用 lua-releng 工具来检测,它是用 perl 语言写的,一般的 linux 环境都是自带 perl 解释器的,所以直接下载下来在要检测的代码目录运行即可。

个别子请求不能配合其他模块指令

ngx.location.capture 和 ngx.location.capture_multi 子请求指令请求的 location 不能包含以下指令 add_before_body、add_after_body、auth_request、echo_location、echo_location_async、echo_subrequest、echo_subrequest_async。

1
2
3
4
5
6
7
8
9
10
11
12
 location /foo {
content_by_lua_block {
res = ngx.location.capture("/bar")
}
}
location /bar {
echo_location /blah;
}
location /blah {
echo "Success!";
}
curl -i http://localhost/foo #失败

介入 nginx 各个阶段

读过 nginx 源码的或者是熟悉nginx运行原理的朋友应该知道,nginx 分为很多阶段,比如重写阶段、访问控制阶段、内容阶段、日志阶段。ngx_lua 的功能指令基本上于各个阶段对应,可以执行对应的业务逻辑,关系图大致如下