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

下面介绍的 Lua API 可以在 *_by_lua_block 和 *_by_lua_file 指定的 Lua 代码里调用,这些 API 很好的桥接了 nginx 和 Lua。

ngx.arg

1
2
syntax: val = ngx.arg[index]
context: body_filter_by_lua*

在响应体过滤时候 ngx.arg[1] 代表响应体内容,ngx.arg[2] 代表该块的标志位。参照 配置指令 body_filter_by_lua_block。

ngx.var.VARIABLE

这个指令可以获取 nginx 的变量,注意,在一次请求里 nginx 变量读取的时候会分配一次内存,在请求结束的时候会释放,所以如果需要反复使用该变量的值,可以保存为本地 lua 变量。

未定义的变量返回 nil,定义了未初始化的返回空字符串。

把变量设置为 nil,会清除该变量。

1
ngx.var.args = nil

例子

1
2
3
4
5
6
7
8
9
10
11
12
location /f {
set $a 1;
set $b 2;
content_by_lua_file test.lua;
}
--test.lua
local t1 = ngx.var.a
local t3 = ngx.var.uri

ngx.say(t1," ", t3)
[root@192 ~]# curl localhost/f
1 /f

print

输出到 error.log 文件,日志级别为 ngx.NOTICE。等同于

1
ngx.log(ngx.NOTICE,...)

nil、true、false、ngx.null(null) 都会以字符串形式输出到日志。默认最大长度为 2048,除非修改 src/core/ngx_log.h 的 NGX_MAX_ERROR_STR 宏。

ngx.log

同上,记录日志,可以指定日志级别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
error_log  logs/error.log  notice;
location /f {
set $a 1;
set $b 2;
content_by_lua_block {
ngx.say("http://www.freecls.com")
ngx.log(ngx.NOTICE,ngx.null," ", nil)
}
#body_filter_by_lua_block {print(ngx.arg[1])}
}
[root@192 ~]# curl localhost/f
http://www.freecls.com
[root@192 openresty]# tail -f logs/error.log

2018/08/01 15:34:39 [notice] 96234#0: *46 [lua] content_by_lua(nginx.conf:49):3: null nil \
, client: 127.0.0.1, server: localhost, request: "GET /f HTTP/1.1", host: "localhost"

ngx.ctx

该表格可以存储每个请求的上下文数据,在同一个请求里共享,生命周期伴随当前请求。

不到非使用不可,建议不要用,因为查询 ngx.ctx 需要调用元方法,比较耗时。

1
2
3
4
5
6
7
8
9
10
11
12
location /test {
rewrite_by_lua_block {
ngx.ctx.foo = 76
}
access_by_lua_block {
ngx.ctx.foo = ngx.ctx.foo + 3
}
content_by_lua_block {
ngx.say(ngx.ctx.foo)
}
}
79

但是子请求会有其自己的上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
location /sub {
content_by_lua_block {
ngx.say("sub pre: ", ngx.ctx.blah)
ngx.ctx.blah = 32
ngx.say("sub post: ", ngx.ctx.blah)
}
}

location /main {
content_by_lua_block {
ngx.ctx.blah = 73
ngx.say("main pre: ", ngx.ctx.blah)
local res = ngx.location.capture("/sub")
ngx.print(res.body)
ngx.say("main post: ", ngx.ctx.blah)
}
}
main pre: 73
sub pre: nil
sub post: 32
main post: 73

内部重定向将会清空 ngx.ctx 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
location /new {
content_by_lua_block {
ngx.say(ngx.ctx.foo)
}
}

location /orig {
content_by_lua_block {
ngx.ctx.foo = "hello"
ngx.exec("/new")
}
}
nil

如果想要在自己的模块函数里操作 ngx.ctx,可以当做参数传递,因为表格传递的是引用,请求结束后会自动销毁。

1
2
3
4
5
6
7
8
-- mymodule.lua
local _M = {}

function _M.main(ctx)
ctx.foo = "bar"
end

return _M

ngx.print

输出响应体,如果响应头没发送,那么会先发送响应头。成功返回1 失败返回 nil 加错误信息。可以输出 nil、true、false 为响应的字符串,ngx.null 会输出成 null。如果传入的是表格,会一个接一个的输出。

ngx.print() 是异步调用,会立马返回,不会等到数据全部存入发送缓冲区。想同步操作,可以在后面跟上 ngx.flush(true)。

但是有一点需要注意,ngx.print 会调用所有的响应体过滤链,同步模式可能会比较耗时。

ngx.say

同上,只是会在最后附上换行符。

1
2
3
4
5
6
7
8
9
10
11
location /f {
set $a 1;
set $b 2;
content_by_lua_block {
ngx.print(1)
ngx.print(2)
ngx.say(3)
ngx.say(4)
}
body_filter_by_lua_block {print(ngx.arg[1])} #记录日志,看看调用了几次
}

由于 ngx.print() 或者 ngx.say() 会一个接一个的输出,所以上面的 body_filter_by_lua* 响应体过滤至少会调用 4 次。

ngx.flush

1
ngx.flush(bool)

刷新响应体内容到系统的发送缓冲区,接收一个bool参数,默认为 false,代表异步,立马返回。如果设为 true,那么为同步模式,要等到所有的内容经过过滤链后才返回,或者是超时时间到了也返回。

ngx.status

设置响应状态码,必须在响应头发送之前。

ngx.exit

1
ngx.exit(status)

当 status >= 200,将会终止当前请求并返回状态码。当 status == 0,只会终止当前阶段,会继续执行下一阶段。status 一般由常量指定,也可以直接用数字代替。

最好配合 return 使用来强调该请求已经结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
location /f {
content_by_lua_block {
ngx.status = ngx.HTTP_GONE
ngx.say("This is our own content")
return ngx.exit(ngx.HTTP_OK)
}
}
[root@192 ~]# curl localhost/f -v
...
>
< HTTP/1.1 410 Gone
< Server: openresty/1.13.6.2
< Date: Wed, 01 Aug 2018 07:50:12 GMT
< Content-Type: application/octet-stream
< Transfer-Encoding: chunked
< Connection: keep-alive
<
This is our own content

ngx.eof

1
syntax: ok, err = ngx.eof()

主动标记响应体的结束,相当于 nginx 里面发送一个有 last_buf 标记的链。如果不是 keepalive,那么会主动让http客户端关闭连接。利用这个特性,我们可以在发送完响应体后让客户端关闭连接,然后我们可以在后台继续处理额外的事情而不必让客户端干等着。

1
2
3
4
5
6
7
8
9
location /f {
keepalive_timeout 0; //关闭keepalive
content_by_lua_block {
ngx.say("got the task!")
ngx.eof() -- 客户端会主动关闭连接

#此时连接已经关闭,可以做一些额外的工作。
}
}

但是如果是创建子请求到上游服务器,必须要配置 upstream 模块忽略客户端连接的关闭,因为在默认的情况下,如果客户端连接关闭,那么 ngx_http_proxy_module 模块会关闭子请求和主请求。所以必须要有如下配置

1
proxy_ignore_client_abort on;

当然后台工作更好的方式是利用 ngx.timer.at 定时器。

ngx.sleep

1
syntax: ngx.sleep(seconds)

非阻塞睡眠,精度到毫秒

1
2
--睡眠1毫秒
ngx.sleep(0.001)

核心常量

1
2
3
4
5
ngx.OK (0)
ngx.ERROR (-1)
ngx.AGAIN (-2)
ngx.DONE (-4)
ngx.DECLINED (-5)

ngx.exit 接受 ngx.OK、ngx.ERROR、ngx.DECLINED。

ngx.null 常量在 Lua 表格中代表 nil。

HTTP 请求方法常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ngx.HTTP_GET
ngx.HTTP_HEAD
ngx.HTTP_PUT
ngx.HTTP_POST
ngx.HTTP_DELETE
ngx.HTTP_OPTIONS (added in the v0.5.0rc24 release)
ngx.HTTP_MKCOL (added in the v0.8.2 release)
ngx.HTTP_COPY (added in the v0.8.2 release)
ngx.HTTP_MOVE (added in the v0.8.2 release)
ngx.HTTP_PROPFIND (added in the v0.8.2 release)
ngx.HTTP_PROPPATCH (added in the v0.8.2 release)
ngx.HTTP_LOCK (added in the v0.8.2 release)
ngx.HTTP_UNLOCK (added in the v0.8.2 release)
ngx.HTTP_PATCH (added in the v0.8.2 release)
ngx.HTTP_TRACE (added in the v0.8.2 release)

状态码常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)
value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)
value = ngx.HTTP_OK (200)
value = ngx.HTTP_CREATED (201)
value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)
value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)
value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)
value = ngx.HTTP_SPECIAL_RESPONSE (300)
value = ngx.HTTP_MOVED_PERMANENTLY (301)
value = ngx.HTTP_MOVED_TEMPORARILY (302)
value = ngx.HTTP_SEE_OTHER (303)
value = ngx.HTTP_NOT_MODIFIED (304)
value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)
value = ngx.HTTP_PERMANENT_REDIRECT (308)
value = ngx.HTTP_BAD_REQUEST (400)
value = ngx.HTTP_UNAUTHORIZED (401)
value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)
value = ngx.HTTP_FORBIDDEN (403)
value = ngx.HTTP_NOT_FOUND (404)
value = ngx.HTTP_NOT_ALLOWED (405)
value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)
value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)
value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)
value = ngx.HTTP_GONE (410)
value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)
value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)
value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)
value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)
value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)
value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501)
value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)
value = ngx.HTTP_SERVICE_UNAVAILABLE (503)
value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)
value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)
value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)

日志级别常量

1
2
3
4
5
6
7
8
9
ngx.STDERR
ngx.EMERG
ngx.ALERT
ngx.CRIT
ngx.ERR
ngx.WARN
ngx.NOTICE
ngx.INFO
ngx.DEBUG