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

下面是 lua-nginx-module 模块的一些配置指令

lua_package_path

1
2
3
syntax: lua_package_path <lua-style-path-str>
default:
context: http

设置 lua 模块的搜索路径,可以包含 $prefix 或 ${prefix} 变量,当我们有多个 openresty 程序通过 -p 命令指定工作目录启动时非常有用。

lua_package_cpath

1
2
3
syntax: lua_package_cpath <lua-style-path-str>
default:
context: http

指定c动态库搜索路径,其他的同上。

1
2
lua_package_cpath '/usr/lib64/lua/5.1/?.so;;/tmp/lua/*.so;;';
lua_package_path '$prefix/lib/*.lua;;/tmp/lua/*.lua;;';

init_by_lua_block

1
context: http

启动nginx时,在每个 worker 进程 fork() 之前调用,如果关闭了 lua_code_cache,那么每次请求都会调用。一般用来预先加载常用的 lua 库。

由于是在 worker fork()调用之前,数据和代码在这里加载会省内存,基于操作系统 "写时拷贝" 特性。

在这里也可以初始化共享内存。

目前只支持 ngx.log、print、ngx.shared.DICT API。

init_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算。

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
init_by_lua_block { require "cjson" }

server {
location = /api {
content_by_lua_block {
-- 直接返回,因为在初始化时候加载过了
-- 已经在 package.loaded 表格里
local cj = require("cjson")
}
}
}
lua_shared_dict dogs 1m;

init_by_lua_block {
local dogs = ngx.shared.dogs;
dogs:set("Tom", 56)
}

server {
location = /api {
content_by_lua_block {
local dogs = ngx.shared.dogs;
ngx.say(dogs:get("Tom"))
}
}
}

init_worker_by_lua_block

1
context: http

每个 worker 进程的初始化,如果关闭了 master,那么它刚好在 init_by_lua* 后面运行。一般用于每个 worker 进程的定时器,后端服务器的健康检查。

init_worker_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

set_by_lua_block

1
2
3
syntax: set_by_lua_block $res <lua-script-str>
context: server, server if, location, location if
phase: rewrite

运行原理是在 ngx_http_rewrite_module 模块里注入自定义指令,是阻塞的,耗时操作不该放在这里面执行。

ngx_http_rewrite_module 不支持非阻塞,所以 lua api 需要配合协程 yield 的不能在此命令里运行,比如

1.输出api函数(ngx.say、ngx.send_headers)
2.控制api函数(ngx.exit)
3.子请求api函数(ngx.location.capture、ngx.location.capture_multi)
4.cosocket api函数(ngx.socket.tcp、ngx.req.socket)
5.sleep api 函数(ngx.sleep)

set_by_lua_file

1
2
3
syntax: set_by_lua_file $res <path-to-lua-script-file> [$arg1 $arg2 ...]
context: server, server if, location, location if
phase: rewrite

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

支持变量,在代码内部可以使用 ngx.arg[1+] 来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
location /foo {
set $diff '';

set_by_lua_block $sum {
local a = 32
local b = 56

ngx.var.diff = a - b; -- 设置 $diff 变量
return a + b; -- 返回到 $sum
}

echo "sum = $sum, diff = $diff";
}

可以混合以下模块指令 ngx_http_rewrite_module、set-misc-nginx-module、array-var-nginx-module modules。

1
2
3
set $foo 32;
set_by_lua $bar 'return tonumber(ngx.var.foo) + 1';
set $baz "bar: $bar"; # $baz == "bar: 33"

rewrite_by_lua_block

1
2
3
syntax: rewrite_by_lua_block { lua-script }
context: http, server, location, location if
phase: rewrite tail

在 nginx 重写阶段执行,但是在标准模块 ngx_http_rewrite_module 之后运行,除非开启 rewrite_by_lua_no_postpone。

1
2
3
4
5
6
7
8
location /foo{
set $a 10; #1
rewrite_by_lua_block {ngx.var.b = tonumber(ngx.var.b) + 10} #4
set $b 0; #2
if($b = '0'){ #3
...
}
}

注意:在 rewrite_by_lua_block 里执行 ngx.exit(ngx.OK) 会继续往下面阶段执行,如果想在重写阶段终止,传入 ngx.exit(status) 的 status >= 200(ngx.HTTP_OK) 和 status < 300(ngx.HTTP_SPECIAL_RESPONSE)--代表成功,或者 ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (或者是它的兄弟状态码比如501、502..)--代表失败。

下面由于 ngx_http_rewrite_module 执行了内部跳转,所以 rewrite_by_lua_block 并不会执行。

1
2
3
4
5
6
7
location /foo {
rewrite ^ /bar; #成功跳转
rewrite_by_lua 'ngx.exit(503)'; #不会执行
}
location /bar {
...
}

如果指定为 break,则会执行。

1
2
3
4
5
6
7
location /foo {
rewrite ^ /bar break; #不执行内部跳转
rewrite_by_lua 'ngx.exit(503)'; #会执行
}
location /bar {
...
}

rewrite_by_lua_file

1
2
3
syntax: rewrite_by_lua_file <path-to-lua-script-file>
context: http, server, location, location if
phase: rewrite tail

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

access_by_lua_block

1
2
3
syntax: access_by_lua_block { lua-script }
context: http, server, location, location if
phase: access tail

在访问控制阶段执行,在 ngx_http_access_module 之后执行。ngx.exit(status) 是否终止该请求同上面的 rewrite。

access_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

content_by_lua_block

1
2
3
syntax: content_by_lua_block { lua-script }
context: location, location if
phase: content

在内容阶段执行。

content_by_lua_file

1
2
3
syntax: content_by_lua_file <path-to-lua-script-file>
context: location, location if
phase: content

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

下面这样是允许的,但是由参数来指定文件很危险,不推荐这样做。

1
2
3
4
location ~ ^/app/([-_a-zA-Z0-9/]+) {
set $path $1;
content_by_lua_file /path/to/lua/app/root/$path.lua;
}

下面这种做法推荐,从url里解析出值存入变量,然后在 index.lua 里通过如 ngx.var.id 来获取。

1
2
3
4
5
6
7
location ~ ^/u/([a-z]+)/?([a-f0-9]*)$ {
default_type text/html;

set $id $2;
set $action $1;
content_by_lua_file html/article/tem/u/index.lua;
}

header_filter_by_lua_block

1
2
3
syntax: header_filter_by_lua <lua-script-str>
context: http, server, location, location if
phase: output-header-filter

响应头过滤阶段,可以修改响应头,下面的api函数当前禁用。

1.输出api函数(ngx.say、ngx.send_headers)
2.控制api函数(ngx.redirect、ngx.exec)
3.子请求api函数(ngx.location.capture、ngx.location.capture_multi)
4.cosocket api函数(ngx.socket.tcp、ngx.req.socket)

下面会新增响应头,如果存在则覆盖。

1
2
3
4
5
6
7
location / {
proxy_pass http://mybackend;
header_filter_by_lua_block {
ngx.header.Foo = "blah"
ngx.header["content-length"] = nil
}
}

header_filter_by_lua_file

1
2
3
syntax: header_filter_by_lua_file <path-to-lua-script-file>
context: http, server, location, location if
phase: output-header-filter

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

body_filter_by_lua_block

1
2
3
syntax: body_filter_by_lua_block { lua-script-str }
context: http, server, location, location if
phase: output-body-filter

响应体块通过 ngx.arg[1] 返回,ngx.arg[2] 为 true 代表响应体内容的结束。

注意:nginx 响应过滤在一个请求里可能会被调用多次(如果以 chunk 传输),因此 lua 代码也会被执行多次。

ngx.arg[2] = true 在 nginx 内部里面即把 last_buf 设为1,last_in_chain 设为1(子请求)。

通过lua字符串或者lua表格里的字符串覆盖 ngx.arg[1] 可以改变响应体,比如下面就是把响应体转成小写再输出。

1
2
3
4
location / {
proxy_pass http://mybackend;
body_filter_by_lua_block {ngx.arg[1] = string.upper(ngx.arg[1])}
}

把 ngx.arg[1] 设置成空lua字符串或者nil,那么将会没有数据块会传入到接下来的响应体过滤模块。

下面在处理第一个块时,检测到 hello 字符串,然后标记为最后一个块,所以接下来的所有块会被忽略,不会返回给客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 location /t {
echo hello world; #第一个块
echo hiya globe; #第二个块

body_filter_by_lua_block {
local chunk = ngx.arg[1]
if string.match(chunk, "hello") then
ngx.arg[2] = true -- 标记该块为最后一个块
return
end

}
}
[root@192 openresty]# curl localhost/t
hello world

ngx.arg[1] = nil 代表丢弃当前块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location /t {
echo hello world; #第一个块
echo hiya globe; #第二个块
echo http://www.freecls.com; #第三个块

body_filter_by_lua_block {
local chunk = ngx.arg[1]
if string.match(chunk, "globe") then
ngx.arg[1] = nil #丢弃第二个块
end
}
}
[root@192 openresty]# curl localhost/t
hello world
http://www.freecls.com

lua代码可能会改变响应体的长度,所以应该在响应头过滤时清除 Content-Length 响应头,那么 nginx 会在响应头里加上 Transfer-Encoding: chunked。

1
2
3
4
location /foo {
header_filter_by_lua_block { ngx.header.content_length = nil }
body_filter_by_lua {ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"}
}

下面的api函数当前禁用。

1.输出api函数(ngx.say、ngx.send_headers)
2.控制api函数(ngx.exit、ngx.exec)
3.子请求api函数(ngx.location.capture、ngx.location.capture_multi)
4.cosocket api函数(ngx.socket.tcp、ngx.req.socket)

body_filter_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

log_by_lua_block

1
2
3
syntax: log_by_lua_block { lua-script }
context: http, server, location, location if
phase: log

日志阶段运行,不会替换当前访问日志,在日志处理阶段之前运行。

下面的api函数当前禁用。

1.输出api函数(ngx.say、ngx.send_headers)
2.控制api函数(ngx.exit)
3.子请求api函数(ngx.location.capture、ngx.location.capture_multi)
4.cosocket api函数(ngx.socket.tcp、ngx.req.socket)

log_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

balancer_by_lua_block

1
2
3
syntax: balancer_by_lua_block { lua-script }
context: upstream
phase: content

该指令可以动态的选择负载均衡的上流服务器,一般配合 ngx_proxy 和 ngx_fastcgi 模块使用。

也可以与上游连接池指令配合比如 keepalive 。在单个 upstream{} 块上,keepalive 指令在 balancer_by_lua_block 指令之后起作用。

配合 lua-resty-core 类库的 ngx.balancer 模块,可以忽略定义在 upstream{} 里所有服务器。

该 lua 代码可能会被执行多次,基于 proxy_next_upstream 指令的设置。

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
36
37
38
39
40
41
http {
upstream backend {
server 0.0.0.1; # just an invalid address as a place holder

balancer_by_lua_block {
local balancer = require "ngx.balancer"

-- 一般在这里会根据一些机制动态生成地址和端口
-- 而不会像这里写死
local host = "127.0.0.2"
local port = 8080

local ok, err = balancer.set_current_peer(host, port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(500)
end
}

keepalive 10; # connection pool
}

server {
# this is the real entry point
listen 80;

location / {
# make use of the upstream named "backend" defined above:
proxy_pass http://backend/fake;
}
}

server {
# this server is just for mocking up a backend peer here...
listen 127.0.0.2:8080;

location = /fake {
echo "this is the fake backend peer...";
}
}
}

balancer_by_lua_file

1
2
3
syntax: balancer_by_lua_file <path-to-lua-script-file>
context: upstream
phase: content

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

lua_capture_error_log

1
2
3
syntax: lua_capture_error_log size
default: none
context: http

设置错误日志的缓冲区大小,单位k、m。一般来说 4K 可以存储20条错误日志。如果错误日志满了,最旧的那条会被移除。

可以通过 lua-resty-core 库的 ngx.errlog 模块的 get_logs() 函数来读取日志,读取了的日志会从缓冲区里移除以便腾出空间给其他错误日志,如果读取的速度够快,则该缓冲区没必要设置的太大。

该缓冲区的日志会受 error_log 指令的日志级别影响,只会捕获到级别大于等于 error_log 指令设置的级别。当然可以使用 errlog.set_filter_level 设置更高的日志级别,很灵活。

lua_use_default_type

1
2
3
syntax: lua_use_default_type on | off
default: lua_use_default_type on
context: http, server, location, location if

是否继承由 default_type 设置的默认 mime 类型,默认开启。

lua_malloc_trim

1
2
3
syntax: lua_malloc_trim <request-count>
default: lua_malloc_trim 1000
context: http

设置内部 libc 运行库多少请求后释放缓存,设置太小会平凡的清理缓存,耗cpu,会节约内存。

因为它是在日志阶段运行的统计,默认只统计主请求,如果设置 log_subrequest on,那么子请求也会统计,这样到达清理缓存的零界点就会加快。

lua_code_cache

1
2
3
syntax: lua_code_cache on | off
default: lua_code_cache on
context: http, server, location, location if

对于 *_by_lua_file 指定的 lua 文件是否开启缓存。如果关闭,每次请求都会重新生成 lua虚拟机,所以改动后立即生效,适合调试阶段,非常不建议在正式环境关闭缓存。

但是如果通过 *_by_lua_block 指令直接在 nginx.conf 里配置的,更新后,还是得 reload 或重启 nginx 后才能生效,因为它属于配置的一部分。

通过 dofile 或者 loadfile 指令加载的 lua 文件不会被缓存。

lua_regex_cache_max_entries

1
2
3
syntax: lua_regex_cache_max_entries <num>
default: lua_regex_cache_max_entries 1024
context: http

设置单个worker进程编译后正则的缓存数,如果在 ngx.re.match、ngx.re.gmatch、ngx.re.sub、ngx.re.gsub 设置了 o 标志,那么就会缓存该正则。

如果超过了该数目,那么超过的正则将不会缓存。

不要在正则字符串会动态变化里设置 o 标志,免得立马就达到正则上限了。

lua_regex_match_limit

1
2
3
syntax: lua_regex_match_limit <num>
default: lua_regex_match_limit 0
context: http

对执行 ngx.re API 设置匹配限制,0代表使用 PCRE 库的默认值。

lua_need_request_body

1
2
3
4
syntax: lua_need_request_body <on|off>
default: off
context: http, server, location, location if
phase: depends on usage

是否读取请求体,默认情况下,nginx 是不读取请求体的。

如果请求体大小没超过 client_body_buffer_size 设置的大小,那么读取后会存入 $request_body 变量,如果超过了,会存入临时文件。

如果当前 location 有 rewrite_by_lua* 指令,那么会在重写阶段,该指令执行之前读取。如果只有 content_by_lua*指令,那么会在内容阶段,该指令执行之前读取。

如果要用到请求体,建议利用 ngx.req.read_body() 来实时读取保存,利用 ngx.req.discard_body() 来主动读取并丢弃请求体。

ssl_certificate_by_lua_block

1
2
3
syntax: ssl_certificate_by_lua_block { lua-script }
context: server
phase: right-before-SSL-handshake

该lua代码会在开始 ssl 握手前执行,一般会配合 lua-resty-core 库的 ngx.ssl 和 ngx.ocsp 模块使用。

后面会有详细例子。

ssl_certificate_by_lua_file

同上,只是用来指定外部 lua 文件,支持相对路径,从当前工作目录开始算,也支持变量。

只会加载一次,所以每次改动需要 reload 才能生效,除非关闭 lua_code_cache。

lua_shared_dict

1
2
3
4
syntax: lua_shared_dict <name> <size>
default: no
context: http
phase: depends on usage

定义共享内存区域,会在所有 worder 进程里共享。单位为 k、m。

1
2
3
4
http {
lua_shared_dict dogs 10m;
...
}

细节在 ngx.shared.DICT 里讲。

lua_socket_connect_timeout

1
2
3
syntax: lua_socket_connect_timeout <time>
default: lua_socket_connect_timeout 60s
context: http, server, location

设置 tcp/unix-domain 连接的超时时间,可以被 settimeout 或者 settimeouts 覆盖。单位m、s、ms。

lua_socket_send_timeout

1
2
3
syntax: lua_socket_send_timeout <time>
default: lua_socket_send_timeout 60s
context: http, server, location

设置 tcp/unix-domain 发送的超时时间,可以被 settimeout 或者 settimeouts 覆盖。单位m、s、ms。

lua_socket_read_timeout

1
2
3
4
syntax: lua_socket_read_timeout <time>
default: lua_socket_read_timeout 60s
context: http, server, location
phase: depends on usage

设置 tcp/unix-domain 接收的超时时间,可以被 settimeout 或者 settimeouts 覆盖。单位m、s、ms。

lua_socket_buffer_size

1
2
3
syntax: lua_socket_buffer_size <size>
default: lua_socket_buffer_size 4k/8k
context: http, server, location

设置 cosocket 读取缓冲区大小。

lua_socket_pool_size

1
2
3
syntax: lua_socket_pool_size <size>
default: lua_socket_pool_size 30
context: http, server, location

设置每个worker进程保存在连接池的连接数,超过了会移除最早的。

lua_socket_keepalive_timeout

1
2
3
syntax: lua_socket_keepalive_timeout <time>
default: lua_socket_keepalive_timeout 60s
context: http, server, location

保存在连接池里的超时连接时间,如果超过了,那么会关闭连接并从连接池移除。

lua_socket_log_errors

1
2
3
syntax: lua_socket_log_errors on|off
default: lua_socket_log_errors on
context: http, server, location

是否开启记录 tcp 或者 udp cosockets 错误日志。

lua_ssl_ciphers

1
2
3
syntax: lua_ssl_ciphers <ciphers>
default: lua_ssl_ciphers DEFAULT
context: http, server, location

指定允许的加密方式,可以通过 openssl ciphers 命令获取所有的加密方式。

lua_ssl_crl

1
2
3
syntax: lua_ssl_crl <file>
default: no
context: http, server, location

设置整数吊销列表文件,pem格式。

lua_ssl_protocols

1
2
3
syntax: lua_ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3]
default: lua_ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2
context: http, server, location

设置 ssl/tls 协议。

lua_ssl_trusted_certificate

1
2
3
syntax: lua_ssl_trusted_certificate <file>
default: no
context: http, server, location

设置用来验证的信任 CA 证书文件,pem格式。

lua_ssl_verify_depth

1
2
3
syntax: lua_ssl_verify_depth <number>
default: lua_ssl_verify_depth 1
context: http, server, location

证书链的验证深度

lua_http10_buffering

1
2
3
syntax: lua_http10_buffering on|off
default: lua_http10_buffering on
context: http, server, location, location-if

是否开启响应体缓冲基于 http 1.0 必须要有确切的 Content-Length 响应头。

如果确切的指定了 Content-length 响应头,不管是显性指定(ngx.send_headers),或者是隐式指定(ngx.say、ngx.print),那么将不会缓冲响应体尽管已经开启。

如果通过流的形式输出大量的响应体数据,比如通过 ngx.flush,那么最好关闭响应体缓冲来节约内存。

rewrite_by_lua_no_postpone

1
2
3
syntax: rewrite_by_lua_no_postpone on|off
default: rewrite_by_lua_no_postpone off
context: http

是否关闭 rewrite_by_lua* 总是在标准 rewrite 模块之后执行。

access_by_lua_no_postpone

1
2
3
syntax: access_by_lua_no_postpone on|off
default: access_by_lua_no_postpone off
context: http

是否关闭 access_by_lua* 总是在标准 access 模块之后执行。

lua_transform_underscores_in_response_headers

1
2
3
syntax: lua_transform_underscores_in_response_headers on|off
default: lua_transform_underscores_in_response_headers on
context: http, server, location, location-if

是否开启在通过 ngx.header.HEADER 库指定响应头时,将下划线 _ 转化成连字符 - 。

1
ngx.header.content_type = 'b'

lua_max_pending_timers

1
2
3
syntax: lua_max_pending_timers <count>
default: lua_max_pending_timers 1024
context: http

指定最大未到期的定时器数量,如果超过了,ngx.timer.at 直接返回 nil,和 "too many pending timers" 错误信息。

lua_max_running_timers

1
2
3
syntax: lua_max_running_timers <count>
default: lua_max_running_timers 256
context: http

指定正在运行的定时器数量上限,也就是正在回调函数里处理。如果超过了,新的到期定时器将不会执行回调函数并报错 "256 lua_max_running_timers are not enough"。