在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):iresty/nginx-lua-module-zh-wiki开源软件地址(OpenSource Url):https://github.com/iresty/nginx-lua-module-zh-wiki开源编程语言(OpenSource Language):开源软件介绍(OpenSource Introduction):Namengx_http_lua_module - 嵌入强有力的 Lua 到 Nginx HTTP 服务中。 该模块不是随着 Nginx 源码发行。 更多请看 安装说明。 Table of Contents
Status生产版本可用 Version该文档描述的 ngx_lua v0.10.7 是2016年11月4号发布。 Synopsis # 设置纯 Lua 扩展库的搜寻路径(';;' 是默认路径):
lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';
# 设置 C 编写的 Lua 扩展模块的搜寻路径(也可以用 ';;'):
lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';
server {
location /lua_content {
# 通过 default_type 设置默认的 MIME 类型:
default_type 'text/plain';
content_by_lua_block {
ngx.say('Hello,world!')
}
}
location /nginx_var {
# 通过 default_type 设置默认的 MIME 类型:
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() -- explicitly read the req body
local data = ngx.req.get_body_data()
if data then
ngx.say("body data:")
ngx.print(data)
return
end
-- body may get buffered in a temp file:
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 {
# 通过 default_type 设置默认的 MIME 类型:
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 / {
lua_need_request_body on;
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
-- tests passed
}
# proxy_pass/fastcgi_pass/etc settings
}
} Description该模块通过标准 Lua5.1 解释器或 LuaJIT 2.0/2.1,把 Lua 嵌入到 Nginx 里面, 并利用 Nginx 子请求,把强大的 Lua 线程(Lua协程)混合到 Nginx 的事件模型中。 与 Apache's mod_lua、Lighttpd's mod_magnet 不同的是, 只要使用这个模块提供的Nginx API for Lua来处理请求上游服务,该模块的 Lua 代码被执行在网络上是 100% 非阻塞的。其中上游请求服务有:MySQL、PostgreSQL、Memcached、Redis或upstream HTTP web 服务等。 至少下面这些 Lua 库、Nginx 模块是可以与 ngx_lua 模块配合使用的:
几乎所有的 Nginx 模块都可以通过 ngx.location.capture 或 ngx.location.capture_multi
与 ngx_lua 模块完成调用,但推荐使用类似 在单个 Nginx worker 中,标准 Lua 或 LuaJIT 的实例在所有请求中是共享使用的,但每个请求上下文是通过轻量的 Lua 协程做到隔离的。 在 Nginx worker 进程中加载 Lua 模块,坚持小内存的使用,即使在重负载下依然如此。 该模块是 Nginx 的 HTTP 子系统插件,所以它只能对 HTTP 环境的下游进行对话(例如:HTTP 0.9/1.0/1.1/2.0, WebSockets等)。 如果你想获得通用的 TCP 下游客户端对话能力,这时应使用 ngx_stream_lua 模块,同样它也是兼容 Lua API 的。 Typical Uses列举部分:
本模块会把你带入一个拥有无限可能的服务端开发新世界,你可以把 Nginx 的各种功能进行自由拼接, 更重要的是,开发门槛并不高,这一切都是用强大轻巧的 Lua 语言来操控。 本模块的脚本有充分的灵活性,并且性能和原生 C 语言编程相比毫不逊色,无论是 CPU 时间还是内存占用方面。当然这里需要启用 LuaJIT 2.x。 其他脚本语言的实现通常很难达到类似性能。 Lua state(Lua VM instance)会被共享给单个 nginx worker 内所有的请求,从而达到最小化内存消耗。 Nginx Compatibility最新模块版本和 Nginx 的兼容列表:
比 Nginx 1.6.0 更老的版本 不 再提供支持。 Installation强烈推荐使用 OpenResty 安装包,它包含了 Nginx, ngx_lua, LuaJIT 2.0/2.1 (或者可选的标准 Lua 5.1解释器),还包含很多强劲、好用的 Nginx 模块。使用一个简单的命令就可以完成基础安装: 当然,ngx_lua 也可以手动的编译到 Nginx 中:
源码编译本模块: wget 'http://nginx.org/download/nginx-1.11.2.tar.gz'
tar -xzvf nginx-1.11.2.tar.gz
cd nginx-1.11.2/
# tell nginx's build system where to find LuaJIT 2.0:
export LUAJIT_LIB=/path/to/luajit/lib
export LUAJIT_INC=/path/to/luajit/include/luajit-2.0
# tell nginx's build system where to find LuaJIT 2.1:
export LUAJIT_LIB=/path/to/luajit/lib
export LUAJIT_INC=/path/to/luajit/include/luajit-2.1
# or tell where to find Lua if using Lua instead:
#export LUA_LIB=/path/to/lua/lib
#export LUA_INC=/path/to/lua/include
# Here we assume Nginx is to be installed under /opt/nginx/.
./configure --prefix=/opt/nginx \
--with-ld-opt="-Wl,-rpath,/path/to/luajit-or-lua/lib" \
--add-module=/path/to/ngx_devel_kit \
--add-module=/path/to/lua-nginx-module
make -j2
make install Building as a dynamic module从 NGINX 1.9.11 开始,你也能编译动态模块了,使用 load_module /path/to/modules/ndk_http_module.so; # assuming NDK is built as a dynamic module too
load_module /path/to/modules/ngx_http_lua_module.so; C Macro Configurations通过 OpenResty 或者 Nginx 内核方式构建该模块,你可以定义下面的 C 宏定义作为可选项提供给 C 编译器:
在 Nginx 或者 OpenResty 启用一个或多个宏定义,只用传给
Installation on Ubuntu 11.10注意:这里推荐使用 LuaJIT 2.1 或 LuaJIT 2.0 替换掉标准 Lua 5.1 解释器。 如果不得不使用标准的 Lua 5.1 解释器,在 Ubuntu 上使用这个命令完成安装: apt-get install -y lua5.1 liblua5.1-0 liblua5.1-0-dev 应该可以正确被安装,除了一个小 "麻烦":
ln -s /usr/lib/x86_64-linux-gnu/liblua5.1.so /usr/lib/liblua.so Community英文邮件列表英文邮件列表: openresty-en 。 中文邮件列表中文邮件列表: openresty 。 Code Repository本项目代码放在github上 openresty/lua-nginx-module。 Bugs and Patches提交bug报告、想法或补丁,可通过下面方式:
Lua/LuaJIT 字节码 support从 请注意,LuaJIT 2.0/2.1 生成的二进制格式与标准 Lua 5.1 解析器是不兼容的。 所以如果在 ngx_lua 下使用 LuaJIT 2.0/2.1,那么 LuaJIT 兼容的二进制文件必须是下面这样生成的: /path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.luac
/path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.luac 对于 http://luajit.org/running.html#opt_b 同样的,由 LuaJIT 2.1 生成的字节码文件对于 LuaJIT 2.0 也是 不 兼容的,反之亦然。 第一次对于 LuaJIT 2.1 版本的字节支持,是在 v0.9.3 上完成的。 近似的,如果使用标准 Lua 5.1 解释器,Lua 的兼容字节码文件必须用 luac 命令行来生成: luac -o /path/to/output_file.luac /path/to/input_file.lua 与 LuaJIT 不太一样, Lua 5.1 的字节码文件是默认包含调试信息的。这里可以使用 luac -s -o /path/to/output_file.luac /path/to/input_file.lua 在使用 LuaJIT 2.0/2.1 的 ngx_lua 实例中试图加载标准 Lua 5.1 字节码文件(反之亦然),将会在 error.log 中记录一条类似的错误信息:
使用 Lua System Environment Variable Support如果你想在 Lua 中通过标准 Lua API os.getenv
来访问系统环境变量,例如 env foo; HTTP 1.0 supportHTTP 1.0 协议不支持分块输出,当响应体不为空时,需要在响应头中明确指定 对于大型流式响应输出,禁用 lua_http10_buffering 以最小化内存占用非常重要。 请注意,一些常见的 HTTP 性能测试工具,例如 Statically Linking Pure Lua Modules当使用 LuaJIT 2.x 时,可以把一个纯 Lua 字节码模块,静态链接到可运行的 Nginx 中。 首先你要使用 用下面这个小例子来印证一下。这里我们的 -- foo.lua
local _M = {}
function _M.go()
print("Hello from foo")
end
return _M 我们把
这里重要的是 然后在构建 Nginx 或者 OpenResty时,
传给 ./configure --with-ld-opt="/path/to/foo.o" ... 最后,你可以在运行在 ngx_lua 中的任意 Lua 代码中调用: local foo = require "foo"
foo.go() 并且,这段代码再也不会依赖外部的 在调用 local foo = require "resty.foo" 那么在你使用 把 这是因为 LuaJIT 字节码格式在不同版本之间可能是不兼容的。 当字节码文件出现了不兼容情况,你将看到一行 Lua 运行时错误信息:没找到 Lua 模块。 当你拥有多个 ./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ... 如果你有非常多的 ar rcus libmyluafiles.a *.o 然后你就可以把 ./configure \
--with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
Data Sharing within an Nginx Worker在同一个 nginx worker 进程处理的所有请求中共享数据,
需要将共享数据封装进一个 Lua 模块中,并使用 Lua 语言内置的 下面是一个完整的例子: -- 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"))
}
} 例子中的 注意,这种数据共享方式是基于worker而不是基于服务器的。 也就是说,当 Nginx 主进程下面有多个 worker 进程时,数据共享不能跨越这些 worker 之间的进程边界。 一般来说,仅推荐使用这种方式共享只读数据。 当计算过程中没有非阻塞性 I/O 操作时(包括 ngx.sleep), 你也可以在 nginx worker 进程内所有并发请求中共享可改变的数据。 只要你不把控制权交还给 nginx 事件循环以及 ngx_lua 的轻量级线程调度器(包括隐含的),它们之间就不会有任何竞争。 因此,当你决定在 worker 中共享可变数据时,一定要非常小心。 错误的优化经常会导致在高负载时产生竞争,这种 bug 非常难以发现。 如果需要在服务器级别共享数据,请使用以下方法:
Known IssuesTCP socket 连接操作遗留问题tcpsock:connect方法,返回 然而,后面尝试对 cosocket 对象的任何操作都将失败,并返回由失效连接操作所产生实际的错误状态消息。 这个问题是由于在 Nginx 的事件模型的局限性,似乎只影响 Mac OS X 系统。 Lua 协程 Yielding/Resuming无论Lua 5.1 and LuaJIT 2.0/2.1,内建的 对于标准 Lua 5.1 解析器的虚拟机唤醒支持是不完善的,ngx.location.capture, ngx.location.capture_multi, ngx.redirect, ngx.exec 和 ngx.exit 方法,在 Lua pcall() 或 xpcall() 中是不能使用的。甚至 Lua Variable Scope在代码中导入模块时应注意一些细节,推介使用如下格式:
而非:
理由如下:从设计上讲,全局环境的生命周期和一个 Nginx 的请求的生命周期是相同的。为了做到请求隔离,每个请求都有自己的Lua全局变量环境。Lua 模块在第一次请求打到服务器上的时候被加载起来,通过 一般来说,在 ngx_lua 的上下文中使用 Lua 全局变量真的不是什么好主意:
所以,我们极力推介在使用变量的时候总是使用 local 来定义以限定起生效范围是有理由的。 为了在你的 Lua 代码中找出所有使用 Lua 全局变量的地方,你可以运行 lua-releng tool 把所有 .lua 源文件检测一遍:
上述输出说明文件 这个工具能保证 Lua 模块中的局部变量全部是用 local 关键字定义过的,否则将会抛出一个运行时异常。这样能阻止类似变量这样的资源的竞争。理由请参考 Data Sharing within |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论