lua: NGINX 的 Lua 脚本支持
安装
您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:
- RedHat Enterprise Linux 7、8、9 和 10
- CentOS 7、8、9
- AlmaLinux 8、9
- Rocky Linux 8、9
- Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-lua
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-lua
通过在 /etc/nginx/nginx.conf 顶部添加以下内容来启用该模块:
load_module modules/ngx_http_lua_module.so;
本文档描述了 nginx-module-lua v0.10.31,于 2026 年 5 月 29 日发布。
-
YouTube 视频 "Hello World HTTP Example with OpenResty/Lua"
-
YouTube 视频 "Write Your Own Lua Modules in OpenResty/Nginx Applications"
-
YouTube 视频 "OpenResty's resty Command-Line Utility Demo"
-
YouTube 视频 "Measure Execution Time of Lua Code Correctly in OpenResty"
-
YouTube 视频 "Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup"
欢迎订阅我们的 官方 YouTube 频道,OpenResty。
概述
# 设置纯 Lua 外部库的搜索路径(';;' 是默认路径):
# 设置用 C 编写的 Lua 外部库的搜索路径(也可以使用 ';;'):
server {
location /lua_content {
# MIME 类型由 default_type 决定:
default_type 'text/plain';
content_by_lua_block {
ngx.say('Hello, world!')
}
}
location /nginx_var {
# MIME 类型由 default_type 决定:
default_type 'text/plain';
# 尝试访问 /nginx_var?a=hello,world
content_by_lua_block {
ngx.say(ngx.var.arg_a)
}
}
location = /request_body {
client_max_body_size 50k;
client_body_buffer_size 50k;
content_by_lua_block {
ngx.req.read_body() -- 显式读取请求体
local data = ngx.req.get_body_data()
if data then
ngx.say("body data:")
ngx.print(data)
return
end
-- 请求体可能会缓存在临时文件中:
local file = ngx.req.get_body_file()
if file then
ngx.say("body is in file ", file)
else
ngx.say("no body found")
end
}
}
# 通过子请求在 Lua 中实现透明非阻塞 I/O
# (更好的方法是使用 cosockets)
location = /lua {
# MIME 类型由 default_type 决定:
default_type 'text/plain';
content_by_lua_block {
local res = ngx.location.capture("/some_other_location")
if res then
ngx.say("status: ", res.status)
ngx.say("body:")
ngx.print(res.body)
end
}
}
location = /foo {
rewrite_by_lua_block {
res = ngx.location.capture("/memc",
{ args = { cmd = "incr", key = ngx.var.uri } }
)
}
proxy_pass http://blah.blah.com;
}
location = /mixed {
rewrite_by_lua_file /path/to/rewrite.lua;
access_by_lua_file /path/to/access.lua;
content_by_lua_file /path/to/content.lua;
}
# 在代码路径中使用 nginx 变量
# 注意:nginx 变量中的内容必须经过仔细过滤,
# 否则会存在很大的安全风险!
location ~ ^/app/([-_a-zA-Z0-9/]+) {
set $path $1;
content_by_lua_file /path/to/lua/app/root/$path.lua;
}
location / {
client_max_body_size 100k;
client_body_buffer_size 100k;
access_by_lua_block {
-- 检查客户端 IP 地址是否在我们的黑名单中
if ngx.var.remote_addr == "132.5.72.3" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- 检查 URI 是否包含不良词汇
if ngx.var.uri and
string.match(ngx.var.request_body, "evil")
then
return ngx.redirect("/terms_of_use.html")
end
-- 测试通过
}
# proxy_pass/fastcgi_pass 等设置
}
}
描述
此模块将 LuaJIT 2.0/2.1 嵌入到 Nginx 中。 它是 OpenResty 的核心组件。如果您使用此模块,那么您实际上是在使用 OpenResty。
自该模块的 v0.10.16 版本以来,不再支持标准 Lua 解释器(也称为 "PUC-Rio Lua")。本文档交替使用 "Lua" 和 "LuaJIT" 来指代 LuaJIT 解释器。
通过利用 Nginx 的子请求,此模块允许将强大的 Lua 线程(称为 Lua "协程")集成到 Nginx 事件模型中。
与 Apache 的 mod_lua 和 Lighttpd 的 mod_magnet 不同,使用此模块执行的 Lua 代码在网络流量上可以 100% 非阻塞,只要使用本模块提供的 Nginx API for Lua 来处理对上游服务(如 MySQL、PostgreSQL、Memcached、Redis 或上游 HTTP Web 服务)的请求。
至少可以与此模块一起使用以下 Lua 库和 Nginx 模块:
- lua-resty-memcached
- lua-resty-mysql
- lua-resty-redis
- lua-resty-dns
- lua-resty-upload
- lua-resty-websocket
- lua-resty-lock
- lua-resty-logger-socket
- lua-resty-lrucache
- lua-resty-string
- ngx_memc
- ngx_postgres
- ngx_redis2
- ngx_redis
- ngx_proxy
- ngx_fastcgi
几乎所有 Nginx 模块都可以通过 ngx.location.capture 或 ngx.location.capture_multi 与此 ngx_lua 模块一起使用,但建议使用这些 lua-resty-* 库,而不是创建子请求以访问 Nginx 上游模块,因为前者通常更灵活且内存效率更高。
Lua 解释器(也称为 "Lua 状态" 或 "LuaJIT VM 实例")在单个 Nginx 工作进程中的所有请求之间共享,以最小化内存使用。请求上下文通过轻量级 Lua 协程进行隔离。
加载的 Lua 模块在 Nginx 工作进程级别持久存在,即使在高负载下,Lua 的内存占用也很小。
此模块被插入到 Nginx 的 "http" 子系统中,因此它只能使用 HTTP 系列的下游通信协议(HTTP 0.9/1.0/1.1/2.0,WebSockets 等)。如果您想与下游客户端进行通用 TCP 通信,则应使用 ngx_stream_lua 模块,该模块提供了兼容的 Lua API。
典型用法
仅举几例:
- 在 Lua 中混合和处理各种 Nginx 上游输出(代理、drizzle、postgres、redis、memcached 等)的输出,
- 在请求实际到达上游后,在 Lua 中进行任意复杂的访问控制和安全检查,
- 以任意方式操作响应头(通过 Lua)
- 从外部存储后端(如 redis、memcached、mysql、postgresql)获取后端信息,并使用该信息动态选择要访问的上游后端,
- 在内容处理程序中编写任意复杂的 Web 应用程序,使用同步但仍然非阻塞的访问数据库后端和其他存储,
- 在重写阶段以 Lua 进行非常复杂的 URL 调度,
- 使用 Lua 实现 Nginx 子请求和任意位置的高级缓存机制。
可能性是无限的,因为该模块允许将 Nginx 中的各种元素结合在一起,并向用户暴露 Lua 语言的强大功能。该模块提供了完整的脚本灵活性,同时在 CPU 时间和内存占用方面提供与本地 C 语言程序相当的性能水平,这要归功于 LuaJIT 2.x。
其他脚本语言实现通常难以匹配这种性能水平。
Nginx 兼容性
此模块的最新版本与以下版本的 Nginx 兼容:
- 1.29.x (最后测试:1.29.8)
- 1.29.x (最后测试:1.29.2)
- 1.27.x (最后测试:1.27.1)
- 1.25.x (最后测试:1.25.1)
- 1.21.x (最后测试:1.21.4)
- 1.19.x (最后测试:1.19.3)
- 1.17.x (最后测试:1.17.8)
- 1.15.x (最后测试:1.15.8)
- 1.14.x
- 1.13.x (最后测试:1.13.6)
- 1.12.x
- 1.11.x (最后测试:1.11.2)
- 1.10.x
- 1.9.x (最后测试:1.9.15)
- 1.8.x
- 1.7.x (最后测试:1.7.10)
- 1.6.x
Nginx 1.6.0(不包括)之前的核心不受支持。
代码库
此项目的代码库托管在 GitHub 上,地址为 openresty/lua-nginx-module。
LuaJIT 字节码支持
观看 YouTube 视频 "Measure Execution Time of Lua Code Correctly in OpenResty"
自 v0.5.0rc32 版本起,所有 *_by_lua_file 配置指令(如 content_by_lua_file)支持直接加载 LuaJIT 2.0/2.1 原始字节码文件:
/path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc
-bg 选项可用于在 LuaJIT 字节码文件中包含调试信息:
/path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc
有关 -b 选项的更多详细信息,请参阅官方 LuaJIT 文档:
https://luajit.org/running.html#opt_b
请注意,LuaJIT 2.1 生成的字节码文件与 LuaJIT 2.0 不 兼容,反之亦然。对 LuaJIT 2.1 字节码的支持首次在 ngx_lua v0.9.3 中添加。
尝试将标准 Lua 5.1 字节码文件加载到链接到 LuaJIT 2.0/2.1 的 ngx_lua 实例中(或反之亦然)将导致 Nginx 错误消息,如下所示:
[error] 13909#0: *1 failed to load Lua inlined code: bad byte-code header in /path/to/test_file.luac
通过 Lua 原语(如 require 和 dofile)加载字节码文件应始终按预期工作。
系统环境变量支持
如果您想通过标准 Lua API os.getenv 在 Lua 中访问系统环境变量,例如 foo,则还应通过 env directive 在 nginx.conf 文件中列出此环境变量名称。例如,
env foo;
HTTP 1.0 支持
HTTP 1.0 协议不支持分块输出,并且在响应体不为空时需要显式的 Content-Length 头,以支持 HTTP 1.0 的保持连接。
因此,当发出 HTTP 1.0 请求并且 lua_http10_buffering 指令设置为 on 时,ngx_lua 将缓存 ngx.say 和 ngx.print 调用的输出,并且还会推迟发送响应头,直到接收到所有响应体输出。
此时,ngx_lua 可以计算主体的总长度并构造适当的 Content-Length 头返回给 HTTP 1.0 客户端。
然而,如果在运行的 Lua 代码中设置了 Content-Length 响应头,则即使 lua_http10_buffering 指令设置为 on,此缓冲也将被禁用。
对于大型流输出响应,重要的是禁用 lua_http10_buffering 指令以最小化内存使用。
请注意,常见的 HTTP 基准测试工具,如 ab 和 http_load 默认发出 HTTP 1.0 请求。
要强制 curl 发送 HTTP 1.0 请求,请使用 -0 选项。
静态链接纯 Lua 模块
使用 LuaJIT 2.x,可以将纯 Lua 模块的字节码静态链接到 Nginx 可执行文件中。
您可以使用 luajit 可执行文件将 .lua Lua 模块文件编译为包含导出字节码数据的 .o 对象文件,然后将 .o 文件直接链接到您的 Nginx 构建中。
下面是一个简单的示例来演示这一点。假设我们有以下名为 foo.lua 的 .lua 文件:
-- foo.lua
local _M = {}
function _M.go()
print("Hello from foo")
end
return _M
然后我们将此 .lua 文件编译为 foo.o 文件:
/path/to/luajit/bin/luajit -bg foo.lua foo.o
这里重要的是 .lua 文件的名称,这决定了您稍后在 Lua 中如何使用此模块。文件名 foo.o 除了 .o 文件扩展名(指示 luajit 使用的输出格式)外没有任何意义。如果您想从生成的字节码中剥离 Lua 调试信息,只需在上面指定 -b 选项即可。
然后在构建 Nginx 或 OpenResty 时,将 --with-ld-opt="foo.o" 选项传递给 ./configure 脚本:
./configure --with-ld-opt="/path/to/foo.o" ...
最后,您可以在任何由 ngx_lua 运行的 Lua 代码中执行以下操作:
local foo = require "foo"
foo.go()
这段代码不再依赖于外部的 foo.lua 文件,因为它已经被编译到 nginx 可执行文件中。
如果您希望在调用 require 时在 Lua 模块名称中使用点,例如
local foo = require "resty.foo"
那么您需要在将 foo.lua 文件编译为 .o 文件之前将其重命名为 resty_foo.lua。
在编译 .lua 文件为 .o 文件时,务必使用与构建 nginx + ngx_lua 相同版本的 LuaJIT。这是因为不同 LuaJIT 版本之间的字节码格式可能不兼容。当字节码格式不兼容时,您将看到 Lua 运行时错误,提示找不到 Lua 模块。
当您有多个 .lua 文件需要编译和链接时,只需在 --with-ld-opt 选项的值中同时指定它们的 .o 文件。例如,
./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...
如果您有太多 .o 文件,则可能不方便在单个命令中命名它们。在这种情况下,您可以为 .o 文件构建一个静态库(或归档),如下所示:
ar rcus libmyluafiles.a *.o
然后,您可以将 myluafiles 库整体链接到您的 nginx 可执行文件中:
./configure \
--with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
其中 /path/to/lib 是包含 libmyluafiles.a 文件的目录的路径。需要注意的是,这里需要使用链接器选项 --whole-archive,因为否则我们的归档将被跳过,因为在 nginx 可执行文件的主要部分中没有提到归档中的符号。
在 Nginx 工作进程中共享数据
要在同一 Nginx 工作进程处理的所有请求之间全局共享数据,请将共享数据封装到 Lua 模块中,使用 Lua 的 require 内置函数导入该模块,然后在 Lua 中操作共享数据。这是可行的,因为所需的 Lua 模块只会加载一次,所有协程将共享该模块的同一副本(包括其代码和数据)。
请注意,强烈不建议使用全局 Lua 变量,因为这可能导致并发请求之间出现意外的竞争条件。
以下是通过 Lua 模块在 Nginx 工作进程中共享数据的小示例:
-- mydata.lua
local _M = {}
local data = {
dog = 3,
cat = 4,
pig = 5,
}
function _M.get_age(name)
return data[name]
end
return _M
然后从 nginx.conf 中访问它:
location /lua {
content_by_lua_block {
local mydata = require "mydata"
ngx.say(mydata.get_age("dog"))
}
}
在此示例中,mydata 模块将仅在对 /lua 的第一次请求时加载并运行,所有后续对同一 Nginx 工作进程的请求将使用模块的重新加载实例以及其中相同的数据副本,直到向 Nginx 主进程发送 HUP 信号以强制重新加载。
这种数据共享技术对于基于此模块的高性能 Lua 应用程序至关重要。
请注意,这种数据共享是基于 每个工作进程 的,而不是基于 每个服务器 的。也就是说,当 Nginx 主进程下有多个工作进程时,数据共享无法跨越这些工作进程之间的进程边界。
通常建议以这种方式共享只读数据。您还可以在每个 Nginx 工作进程的所有并发请求之间共享可变数据,只要在计算过程中没有 非阻塞 I/O 操作(包括 ngx.sleep)。只要您不将控制权交还给 Nginx 事件循环和 ngx_lua 的轻量级线程调度程序(即使是隐式的),就不会出现任何竞争条件。因此,在希望在工作进程级别共享可变数据时,请务必非常小心。错误的优化很容易在负载下导致难以调试的竞争条件。
如果需要全服务器范围的数据共享,则可以使用以下一种或多种方法:
- 使用本模块提供的 ngx.shared.DICT API。
- 仅使用单个 Nginx 工作进程和单个服务器(但在单台机器上有多核 CPU 或多个 CPU 时不推荐这样做)。
- 使用数据存储机制,如
memcached、redis、MySQL或PostgreSQL。OpenResty 官方发布 附带一组伴随的 Nginx 模块和 Lua 库,这些模块和库提供与这些数据存储机制的接口。
已知问题
TCP 套接字连接操作问题
tcpsock:connect 方法可能会指示 success,尽管存在连接失败,例如 Connection Refused 错误。
然而,稍后尝试操作 cosocket 对象将失败并返回实际的错误状态消息,该消息由失败的连接操作生成。
此问题是由于 Nginx 事件模型的限制造成的,仅在 Mac OS X 上出现。
Lua 协程的让步/恢复
- 由于 Lua 的
dofile和require内置函数当前在 LuaJIT 2.0/2.1 中实现为 C 函数,如果通过dofile或require加载的 Lua 文件在 Lua 文件的 顶层 范围内调用 ngx.location.capture*、ngx.exec、ngx.exit 或其他需要让步的 API 函数,则会引发 Lua 错误 "attempt to yield across C-call boundary"。为避免此问题,请将这些需要让步的调用放入 Lua 文件中的自定义 Lua 函数中,而不是放在文件的顶层范围内。
Lua 变量作用域
导入模块时必须小心,应使用以下形式:
local xxx = require('xxx')
而不是旧的弃用形式:
require('xxx')
原因如下:根据设计,全局环境的生命周期与与之关联的 Nginx 请求处理程序完全相同。每个请求处理程序都有自己的一组 Lua 全局变量,这就是请求隔离的理念。Lua 模块实际上是由第一个 Nginx 请求处理程序加载的,并由 require() 内置函数在 package.loaded 表中缓存以供后续引用,而某些 Lua 模块使用的 module() 内置函数具有将全局变量设置为加载的模块表的副作用。但此全局变量将在请求处理程序结束时被清除,并且每个后续请求处理程序都有自己(干净的)全局环境。因此,将会因为访问 nil 值而引发 Lua 异常。
在 ngx_lua 上下文中使用 Lua 全局变量通常是不明智的,因为:
- Lua 全局变量的误用对并发请求产生不利影响,而这些变量应该是局部作用域的,
- Lua 全局变量需要在全局环境中进行 Lua 表查找,这在计算上是昂贵的,并且
- 某些 Lua 全局变量引用可能包含打字错误,使其难以调试。
因此,强烈 建议始终在适当的局部作用域内声明这些变量。
-- 避免
foo = 123
-- 推荐
local foo = 123
-- 避免
function foo() return 123 end
-- 推荐
local function foo() return 123 end
要查找 Lua 代码中的所有 Lua 全局变量实例,请在所有 .lua 源文件上运行 lua-releng 工具:
$ lua-releng
Checking use of Lua global variables in file lib/foo/bar.lua ...
1 [1489] SETGLOBAL 7 -1 ; contains
55 [1506] GETGLOBAL 7 -3 ; setvar
3 [1545] GETGLOBAL 3 -4 ; varexpand
输出表示 lib/foo/bar.lua 文件的第 1489 行写入了名为 contains 的全局变量,第 1506 行读取了全局变量 setvar,第 1545 行读取了全局变量 varexpand。
此工具将确保 Lua 模块函数中的局部变量都用 local 关键字声明,否则将抛出运行时异常。它防止在访问此类变量时出现不良竞争条件。有关此背后的原因,请参见 在 Nginx 工作进程中共享数据。
其他模块的子请求指令配置的地点
ngx.location.capture 和 ngx.location.capture_multi 指令无法捕获包含 add_before_body、add_after_body、auth_request、echo_location、echo_location_async、echo_subrequest 或 echo_subrequest_async 指令的地点。
location /foo {
content_by_lua_block {
res = ngx.location.capture("/bar")
}
}
location /bar {
echo_location /blah;
}
location /blah {
echo "Success!";
}
$ curl -i http://example.com/foo
将无法按预期工作。
Cosockets 并非在所有地方均可用
由于 Nginx 核心的内部限制,cosocket API 在以下上下文中被禁用:set_by_lua*、log_by_lua*、header_filter_by_lua* 和 body_filter_by_lua。
目前,cosockets 也在 init_by_lua* 和 init_worker_by_lua* 指令上下文中被禁用,但我们可能会在未来为这些上下文添加支持,因为 Nginx 核心没有限制(或者限制可能会被解决)。
然而,当原始上下文不需要等待 cosocket 结果时,存在解决方法。也就是说,通过 ngx.timer.at API 创建一个零延迟计时器,并在计时器处理程序中处理 cosocket 结果,该处理程序与创建计时器的原始上下文异步运行。
特殊转义序列
注意 自 v0.9.17 版本发布以来,可以通过使用 *_by_lua_block {} 配置指令来避免这个陷阱。
PCRE 序列如 \d、\s 或 \w 需要特别注意,因为在字符串字面量中,反斜杠字符 \ 会被 Lua 语言解析器和 Nginx 配置文件解析器在处理之前剥离,如果不在 *_by_lua_block {} 指令中。因此,以下代码片段将无法按预期工作:
# nginx.conf
? location /test {
? content_by_lua '
? local regex = "\d+" -- 这在 *_by_lua_block 指令外部是错误的
? local m = ngx.re.match("hello, 1234", regex)
? if m then ngx.say(m[0]) else ngx.say("not matched!") end
? ';
? }
# 评估为 "not matched!"
为避免此问题,双重 转义反斜杠:
# nginx.conf
location /test {
content_by_lua '
local regex = "\\\\d+"
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
';
}
# 评估为 "1234"
在这里,\\\\d+ 被 Nginx 配置文件解析器剥离为 \\d+,然后在运行之前被 Lua 语言解析器进一步剥离为 \d+。
或者,可以通过将正则表达式模式放在长括号 Lua 字符串字面量中来呈现,例如用 "长括号" [[...]] 包围,这样反斜杠只需在 Nginx 配置文件解析器中转义一次。
# nginx.conf
location /test {
content_by_lua '
local regex = [[\\d+]]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
';
}
# 评估为 "1234"
在这里,[[\\d+]] 被 Nginx 配置文件解析器剥离为 [[\d+]],并且正确处理。
请注意,如果正则表达式模式包含 [...] 序列,则可能需要使用长括号的更长形式 [=[...]=]。如果需要,可以将 [=[...]=] 形式用作默认形式。
# nginx.conf
location /test {
content_by_lua '
local regex = [=[[0-9]+]=]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
';
}
# 评估为 "1234"
转义 PCRE 序列的另一种方法是确保将 Lua 代码放置在外部脚本文件中,并使用各种 *_by_lua_file 指令执行。
使用此方法时,反斜杠仅会被 Lua 语言解析器剥离,因此只需转义一次。
-- test.lua
local regex = "\\d+"
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
-- 评估为 "1234"
在外部脚本文件中,作为长括号 Lua 字符串字面量呈现的 PCRE 序列不需要修改。
-- test.lua
local regex = [[\d+]]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
-- 评估为 "1234"
如前所述,在 *_by_lua_block {} 指令中呈现的 PCRE 序列(在 v0.9.17 版本发布后可用)不需要修改。
# nginx.conf
location /test {
content_by_lua_block {
local regex = [[\d+]]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
}
}
# 评估为 "1234"
注意 建议在 Lua 代码很长时使用 by_lua_file。
不支持与 SSI 混合
在同一 Nginx 请求中不支持将 SSI 与 ngx_lua 混合使用。只需独占使用 ngx_lua。您可以使用 SSI 完成的所有操作都可以在 ngx_lua 上完成,并且使用 ngx_lua 时可能更高效。
SPDY 模式不完全支持
某些由 ngx_lua 提供的 Lua API 在 Nginx 的 SPDY 模式下尚不工作:ngx.location.capture、ngx.location.capture_multi 和 ngx.req.socket。
短路请求缺少数据
Nginx 可能会提前终止请求,至少会返回:
- 400(错误请求)
- 405(不允许)
- 408(请求超时)
- 413(请求实体过大)
- 414(请求 URI 过大)
- 494(客户端关闭请求)
- 500(内部服务器错误)
- 501(未实现)
这意味着通常运行的阶段会被跳过,例如重写或访问阶段。这也意味着无论如何运行的后续阶段,例如 log_by_lua,将无法访问通常在这些阶段中设置的信息。
变更
此模块每个版本的更改在 OpenResty 包的变更日志中列出:
https://openresty.org/#Changes
测试套件
运行测试套件需要以下依赖项:
-
Nginx 版本 >= 1.4.2
-
Perl 模块:
- Test::Nginx: https://github.com/openresty/test-nginx
-
Nginx 模块:
- ngx_devel_kit
- ngx_set_misc
- ngx_auth_request(如果您使用的是 Nginx 1.5.4 及更高版本,则不需要此模块)。
- ngx_echo
- ngx_memc
- ngx_srcache
- ngx_lua(即此模块)
- ngx_lua_upstream
- ngx_headers_more
- ngx_drizzle
- ngx_rds_json
- ngx_coolkit
- ngx_redis2
配置时添加这些模块的顺序很重要,因为过滤链中任何过滤器模块的位置决定最终输出。例如,正确的添加顺序如上所示。
-
第三方 Lua 库:
-
应用程序:
- mysql:创建数据库 'ngx_test',授予用户 'ngx_test' 所有权限,密码为 'ngx_test'
- memcached:监听默认端口 11211。
- redis:监听默认端口 6379。
有关设置测试环境的更多详细信息,请参阅 开发人员构建脚本。
要在默认测试模式下运行整个测试套件:
cd /path/to/lua-nginx-module
export PATH=/path/to/your/nginx/sbin:$PATH
prove -I/path/to/test-nginx/lib -r t
要运行特定测试文件:
cd /path/to/lua-nginx-module
export PATH=/path/to/your/nginx/sbin:$PATH
prove -I/path/to/test-nginx/lib t/002-content.t t/003-errors.t
要在特定测试文件中运行特定测试块,请在要运行的测试块中添加行 --- ONLY,然后使用 prove 工具运行该 .t 文件。
还有各种基于 mockeagain、valgrind 等的测试模式。有关各种高级测试模式的更多详细信息,请参阅 Test::Nginx 文档。另请参阅在 Amazon EC2 上运行的 Nginx 测试集群的测试报告:https://qa.openresty.org。
另请参阅
博客文章:
- Lua-Land CPU Flame Graphs 介绍
- OpenResty 和 Nginx 如何分配和管理内存
- OpenResty 和 Nginx 共享内存区域如何消耗 RAM
- OpenResty 和 Nginx 的共享内存区域中的内存碎片
其他相关模块和库:
- ngx_stream_lua_module 是此模块的官方移植,适用于 Nginx "stream" 子系统(进行通用下游 TCP 通信)。
- lua-resty-memcached 基于 ngx_lua cosocket 的库。
- lua-resty-redis 基于 ngx_lua cosocket 的库。
- lua-resty-mysql 基于 ngx_lua cosocket 的库。
- lua-resty-upload 基于 ngx_lua cosocket 的库。
- lua-resty-dns 基于 ngx_lua cosocket 的库。
- lua-resty-websocket 基于 ngx_lua cosocket 的 WebSocket 服务器和客户端库。
- lua-resty-string 基于 LuaJIT FFI 的库。
- lua-resty-lock 用于非阻塞简单锁 API 的库。
- lua-resty-cookie 用于 HTTP cookie 操作的库。
- 根据 URI 参数路由请求到不同的 MySQL 查询
- 基于 Redis 和 Lua 的动态路由
- 在 ngx_lua 中使用 LuaRocks
- ngx_lua 介绍
- ngx_devel_kit
- echo-nginx-module
- drizzle-nginx-module
- postgres-nginx-module
- memc-nginx-module
- OpenResty 套件
- Nginx Systemtap 工具包
指令
- lua_load_resty_core
- lua_capture_error_log
- lua_use_default_type
- lua_malloc_trim
- lua_code_cache
- lua_thread_cache_max_entries
- lua_regex_cache_max_entries
- lua_regex_match_limit
- lua_package_path
- lua_package_cpath
- init_by_lua
- init_by_lua_block
- init_by_lua_file
- init_worker_by_lua
- init_worker_by_lua_block
- init_worker_by_lua_file
- exit_worker_by_lua_block
- exit_worker_by_lua_file
- set_by_lua
- set_by_lua_block
- set_by_lua_file
- precontent_by_lua_block
- precontent_by_lua_file
- content_by_lua
- content_by_lua_block
- content_by_lua_file
- server_rewrite_by_lua_block
- server_rewrite_by_lua_file
- rewrite_by_lua
- rewrite_by_lua_block
- rewrite_by_lua_file
- access_by_lua
- access_by_lua_block
- access_by_lua_file
- header_filter_by_lua
- header_filter_by_lua_block
- header_filter_by_lua_file
- body_filter_by_lua
- body_filter_by_lua_block
- body_filter_by_lua_file
- log_by_lua
- log_by_lua_block
- log_by_lua_file
- balancer_by_lua_block
- balancer_by_lua_file
- balancer_keepalive
- lua_need_request_body
- ssl_client_hello_by_lua_block
- ssl_client_hello_by_lua_file
- ssl_certificate_by_lua_block
- ssl_certificate_by_lua_file
- ssl_session_fetch_by_lua_block
- ssl_session_fetch_by_lua_file
- ssl_session_store_by_lua_block
- ssl_session_store_by_lua_file
- proxy_ssl_certificate_by_lua_block
- proxy_ssl_certificate_by_lua_file
- proxy_ssl_verify_by_lua_block
- proxy_ssl_verify_by_lua_file
- lua_shared_dict
- lua_socket_connect_timeout
- lua_socket_send_timeout
- lua_socket_send_lowat
- lua_socket_read_timeout
- lua_socket_buffer_size
- lua_socket_pool_size
- lua_socket_keepalive_timeout
- lua_socket_log_errors
- lua_ssl_ciphers
- lua_ssl_crl
- lua_ssl_protocols
- lua_ssl_certificate
- lua_ssl_certificate_key
- lua_ssl_trusted_certificate
- lua_ssl_verify_depth
- lua_ssl_key_log
- lua_ssl_conf_command
- lua_upstream_skip_openssl_default_verify
- lua_http10_buffering
- rewrite_by_lua_no_postpone
- access_by_lua_no_postpone
- precontent_by_lua_no_postpone
- lua_transform_underscores_in_response_headers
- lua_check_client_abort
- lua_max_pending_timers
- lua_max_running_timers
- lua_sa_restart
- lua_worker_thread_vm_pool_size
脚本 NGINX 与 Lua 的基本构建块是指令。指令用于指定用户 Lua 代码的运行时机以及结果将如何使用。以下是指令执行顺序的图示。

lua_load_resty_core
语法: lua_load_resty_core on|off
默认: lua_load_resty_core on
上下文: http
自该模块的 v0.10.16 版本以来,此指令已被弃用。来自 lua-resty-core 的 resty.core 模块现在在 Lua VM 初始化期间强制加载。指定此指令将无效。
此指令首次引入于 v0.10.15 版本,并用于可选加载 resty.core 模块。
lua_capture_error_log
语法: lua_capture_error_log size
默认: 无
上下文: http
启用指定 size 的缓冲区以捕获所有 Nginx 错误日志消息数据(不仅仅是由此模块或 Nginx http 子系统生成的,而是所有内容),而不接触文件或磁盘。
您可以在 size 值中使用单位,如 k 和 m,例如
lua_capture_error_log 100k;
作为经验法则,4KB 的缓冲区通常可以容纳大约 20 条典型的错误日志消息。所以请自行计算!
此缓冲区永远不会增长。如果已满,新错误日志消息将替换缓冲区中的最旧消息。
缓冲区的大小必须大于单个错误日志消息的最大长度(在 OpenResty 中为 4K,在标准 NGINX 中为 2K)。
您可以通过 Lua 端的 get_logs() 函数读取缓冲区中的消息,该函数属于 ngx.errlog 模块,来自 lua-resty-core 库。此 Lua API 函数将返回捕获的错误日志消息,并且还会从全局捕获缓冲区中删除这些已读取的消息,为任何新的错误日志数据腾出空间。因此,如果用户读取缓冲的错误日志数据足够快,则不应将此缓冲区配置得太大。
请注意,标准 error_log 指令中指定的日志级别 确实 对此捕获功能有影响。它仅捕获日志级别不低于 error_log 指令中指定的日志级别的日志消息。 用户仍然可以选择通过 Lua API 函数 errlog.set_filter_level 动态设置更高的过滤日志级别。因此,它比静态的 error_log 指令更灵活。
值得注意的是,无法捕获调试日志,除非使用 ./configure 选项 --with-debug 构建 OpenResty 或 Nginx。并且在生产构建中强烈不建议启用调试日志,因为开销很大。
此指令首次引入于 v0.10.9 版本。
lua_use_default_type
语法: lua_use_default_type on | off
默认: lua_use_default_type on
上下文: http, server, location, location if
指定是否使用 default_type 指令指定的 MIME 类型作为 Content-Type 响应头的默认值。如果不希望 Lua 请求处理程序的默认 Content-Type 响应头,则应停用此指令。
此指令默认开启。
此指令首次引入于 v0.9.1 版本。
lua_malloc_trim
语法: lua_malloc_trim
默认: lua_malloc_trim 1000
上下文: http
请求底层 libc 运行时库在 Nginx 核心处理的每 N 个请求后将其缓存的空闲内存释放回操作系统。默认情况下,N 为 1000。您可以使用自己的数字配置请求计数。较小的数字意味着更频繁的释放,这可能会导致更高的 CPU 时间消耗和较小的内存占用,而较大的数字通常会导致较小的 CPU 时间开销和相对较大的内存占用。根据您的用例调整数字。
将参数配置为 0 实际上会完全关闭定期内存修整。
lua_malloc_trim 0; # 完全关闭修整
当前实现使用 Nginx 日志阶段处理程序进行请求计数。因此,nginx.conf 中的 log_subrequest on 指令的出现可能会在涉及子请求时加快计数。默认情况下,仅“主请求”计数。
请注意,此指令 不 影响由 LuaJIT 自己的分配器基于 mmap 系统调用分配的内存。
此指令首次引入于 v0.10.7 版本。
lua_code_cache
语法: lua_code_cache on | off
默认: lua_code_cache on
上下文: http, server, location, location if
启用或禁用 *_by_lua_file 指令(如 set_by_lua_file 和 content_by_lua_file)和 Lua 模块的 Lua 代码缓存。
关闭时,由 ngx_lua 提供的每个请求将在单独的 Lua VM 实例中运行,自 0.9.3 版本起。这样,在 set_by_lua_file、content_by_lua_file、access_by_lua_file 等中引用的 Lua 文件将不会被缓存,所有使用的 Lua 模块将从头加载。在这种情况下,开发人员可以采用编辑和刷新的方法。
但是请注意,直接在 nginx.conf 中编写的内联 Lua 代码(如通过 set_by_lua、content_by_lua、access_by_lua 和 [rewrite_by_lua](#rewrite_by_lua)指定的代码)在您编辑 nginx.conf 文件中的内联 Lua 代码时不会更新,因为只有 Nginx 配置文件解析器可以正确解析 nginx.conf 文件,唯一的方法是通过发送 HUP 信号重新加载配置文件或重新启动 Nginx。
即使启用代码缓存,通过 dofile 或 loadfile 加载的 Lua 文件也无法被缓存(除非您自己缓存结果)。通常,您可以使用 init_by_lua 或 init_by_lua_file 指令加载所有这些文件,或者只需将这些 Lua 文件作为真正的 Lua 模块并通过 require 加载它们。
ngx_lua 模块不支持 Apache mod_lua 模块中可用的 stat 模式(尚未)。
禁用 Lua 代码缓存强烈不建议用于生产用途,仅在开发期间使用,因为这对整体性能有显著的负面影响。例如,“hello world” Lua 示例的性能在禁用 Lua 代码缓存后可能下降一个数量级。
lua_thread_cache_max_entries
语法: lua_thread_cache_max_entries
默认: lua_thread_cache_max_entries 1024
上下文: http
指定工作进程级别 Lua 线程对象缓存中允许的最大条目数。
此缓存在所有“轻线程”之间回收 Lua 线程 GC 对象。
<num> 的零值禁用缓存。
请注意,此功能需要 OpenResty 的 LuaJIT 和新的 C API lua_resetthread。
此功能首次引入于 v0.10.9 版本。
lua_regex_cache_max_entries
语法: lua_regex_cache_max_entries
默认: lua_regex_cache_max_entries 1024
上下文: http
指定工作进程级别编译的正则表达式缓存中允许的最大条目数。
在 ngx.re.match、ngx.re.gmatch、ngx.re.sub 和 ngx.re.gsub 中使用的正则表达式将在此缓存中缓存,如果指定了正则表达式选项 o(即编译一次标志)。
允许的默认条目数为 1024,当达到此限制时,将不再缓存新的正则表达式(就像未指定 o 选项一样),并且在 error.log 文件中将出现一条警告:
2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ...
如果您使用的是通过加载 resty.core.regex 模块(或仅加载 resty.core 模块)实现的 lua-resty-core 的 ngx.re.* 实现,则在此处使用 LRU 缓存。
请勿对动态生成的正则表达式(和/或 ngx.re.sub 和 ngx.re.gsub 的替换字符串参数)启用 o 选项,以避免达到指定限制。
lua_regex_match_limit
语法: lua_regex_match_limit
默认: lua_regex_match_limit 0
上下文: http
指定 PCRE 库在执行 ngx.re API 时使用的“匹配限制”。引用 PCRE 手册,“限制...会限制可以进行的回溯量”。
当达到限制时,错误字符串 "pcre_exec() failed: -8" 将由 ngx.re API 函数返回。
将限制设置为 0 时,将使用编译 PCRE 库时的默认“匹配限制”。这是此指令的默认值。
此指令首次引入于 v0.8.5 版本。
lua_package_path
语法: lua_package_path
默认: LUA_PATH 环境变量的内容或 Lua 的编译默认值。
上下文: http
设置 Lua 模块搜索路径,用于通过 set_by_lua、content_by_lua 等指定的脚本。路径字符串采用标准 Lua 路径格式,并且可以使用 ;; 代表原始搜索路径。
自 v0.5.0rc29 版本起,搜索路径字符串中可以使用特殊符号 $prefix 或 ${prefix} 来表示由 -p PATH 命令行选项启动 Nginx 服务器时确定的 server prefix 的路径。
lua_package_cpath
语法: lua_package_cpath
默认: LUA_CPATH 环境变量的内容或 Lua 的编译默认值。
上下文: http
设置 Lua C 模块搜索路径,用于通过 set_by_lua、content_by_lua 等指定的脚本。cpath 字符串采用标准 Lua cpath 格式,并且可以使用 ;; 代表原始 cpath。
自 v0.5.0rc29 版本起,搜索路径字符串中可以使用特殊符号 $prefix 或 ${prefix} 来表示由 -p PATH 命令行选项启动 Nginx 服务器时确定的 server prefix 的路径。
init_by_lua
语法: init_by_lua
上下文: http
阶段: 加载配置
注意 自 v0.9.17 版本以来不建议使用此指令。请改用 init_by_lua_block 指令。
类似于 init_by_lua_block 指令,但接受 Nginx 字符串字面量中的 Lua 源代码(需要特殊字符转义)。
例如,
init_by_lua '
print("I need no extra escaping here, for example: \r\nblah")
'
此指令首次引入于 v0.5.5 版本。
init_by_lua_block
语法: init_by_lua_block { lua-script }
上下文: http
阶段: 加载配置
当 Nginx 接收到 HUP 信号并开始重新加载配置文件时,Lua VM 也将被重新创建,init_by_lua_block 将在新的 Lua VM 上再次运行。如果 lua_code_cache 指令关闭(默认开启),则 init_by_lua_block 处理程序将在每个请求上运行,因为在这种特殊模式下,每个请求始终会创建一个独立的 Lua VM。
通常,您可以通过此钩子在服务器启动时预加载 Lua 模块,并利用现代操作系统的写时复制(COW)优化。以下是预加载 Lua 模块的示例:
# 这在分叉出 Nginx 工作进程之前运行:
init_by_lua_block { require "cjson" }
server {
location = /api {
content_by_lua_block {
-- 以下 require() 将仅返回
-- 来自 package.loaded 的已加载模块:
ngx.say(require "cjson".encode{dog = 5, cat = 6})
}
}
}
您还可以在此阶段初始化 lua_shared_dict shm 存储。以下是此示例:
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"))
}
}
}
但请注意, lua_shared_dict 的 shm 存储不会通过配置重新加载(例如,通过 HUP 信号)。因此,如果您不想在 init_by_lua_block 代码中重新初始化 shm 存储,则只需在 shm 存储中设置自定义标志,并始终检查该标志。
因为此上下文中的 Lua 代码在 Nginx 分叉其工作进程(如果有的话)之前运行,因此在此处加载的数据或代码将享有许多操作系统提供的 写时复制(COW) 特性,从而节省大量内存。
请勿在此上下文中初始化自己的 Lua 全局变量,因为使用 Lua 全局变量会产生性能损失并导致全局命名空间污染(有关详细信息,请参见 Lua 变量作用域 部分)。推荐的方法是使用适当的 Lua 模块 文件(但不要使用标准 Lua 函数 module() 来定义 Lua 模块,因为它也会污染全局命名空间)并在 init_by_lua_block 或其他上下文中调用 require() 来加载自己的模块文件。
此上下文中仅支持一小部分 Nginx API for Lua:
- 日志 API: ngx.log 和 print,
- 共享字典 API: ngx.shared.DICT。
在此上下文中,可能会根据未来用户请求支持更多 Nginx API for Lua。
基本上,您可以安全地在此上下文中使用阻塞 I/O 的 Lua 库,因为在服务器启动期间阻塞主进程是完全可以接受的。即使 Nginx 核心在配置加载阶段也会进行阻塞 I/O(至少在解析上游主机名时)。
您应对在此上下文中注册的 Lua 代码中的潜在安全漏洞保持高度警惕,因为 Nginx 主进程通常以 root 账户运行。
此指令首次引入于 v0.9.17 版本。
有关 OpenResty 和 Nginx 共享内存区域的更多详细信息,请参阅以下博客文章:
init_by_lua_file
语法: init_by_lua_file
上下文: http
阶段: 加载配置
等效于 init_by_lua_block,但指定的文件 <path-to-lua-script-file> 包含要执行的 Lua 代码或 Lua 字节码文件。
当给定相对路径如 foo/bar.lua 时,它们将被转换为相对于启动 Nginx 服务器时确定的 server prefix 路径的绝对路径。
此指令首次引入于 v0.5.5 版本。
init_worker_by_lua
语法: init_worker_by_lua
上下文: http
阶段: 启动工作进程
注意 自 v0.9.17 版本以来不建议使用此指令。请改用 init_worker_by_lua_block 指令。
类似于 init_worker_by_lua_block 指令,但接受 Nginx 字符串字面量中的 Lua 源代码(需要特殊字符转义)。
例如,
init_worker_by_lua '
print("I need no extra escaping here, for example: \r\nblah")
';
此指令首次引入于 v0.9.5 版本。
此钩子不再在缓存管理器和缓存加载器进程中运行,自 v0.10.12 版本起。
init_worker_by_lua_block
语法: init_worker_by_lua_block { lua-script }
上下文: http
阶段: 启动工作进程
在每个 Nginx 工作进程启动时运行指定的 Lua 代码,当主进程启用时。如果主进程禁用,则此钩子将在 init_by_lua* 之后运行。
此钩子通常用于创建每个工作进程的重复计时器(通过 ngx.timer.at Lua API),无论是用于后端健康检查还是其他定时例行工作。以下是一个示例,
init_worker_by_lua_block {
local delay = 3 -- 以秒为单位
local new_timer = ngx.timer.at
local log = ngx.log
local ERR = ngx.ERR
local check
check = function(premature)
if not premature then
-- 执行健康检查或其他例行工作
local ok, err = new_timer(delay, check)
if not ok then
log(ERR, "创建计时器失败: ", err)
return
end
end
-- 在计时器中执行某些操作
end
local hdl, err = new_timer(delay, check)
if not hdl then
log(ERR, "创建计时器失败: ", err)
return
end
-- 在 init_worker_by_lua 中的其他工作
}
此指令首次引入于 v0.9.17 版本。
此钩子不再在缓存管理器和缓存加载器进程中运行,自 v0.10.12 版本起。
init_worker_by_lua_file
语法: init_worker_by_lua_file
上下文: http
阶段: 启动工作进程
类似于 [init_worker_by_lua_block




