Lua是一门脚本动态语言,并不太适宜做复杂业务逻辑的程序开发,然而linux 分区,在高并发场景下,NginxLua编程是解决性能问题的神器。
NginxLua编程主要的应用场景如下:
ngx_lua是Nginx的一个扩充模块,将LuaVM嵌入Nginx,恳求时创建一个VM,恳请结束时回收VM,这样就可以在Nginx内部运行Lua脚本linux启动nginx命令,致使Nginx弄成一个Web容器。以OpenResty为例,其提供了一些常用的ngx_lua开发模块:
Lua脚本须要通过Lua协程来解释执行,不仅Lua官方的默认类库外,目前使用广泛的Lua类库称作LuaJIT。LuaJIT采用C语言编撰,被设计成全兼容标准Lua5.1,因而LuaJIT代码的句型和标准Lua的句型没多大区别。并且LuaJIT的运行速率比标准Lua快数十倍。
NginxLua的执行原理
在OpenResty中,每位Worker进程使用一个LuaVM,当恳求被分配到Worker时,将在这个LuaVM中创建一个解释器,解释器之间数据隔离,每位轮询都具有独立的全局变量。
ngx_lua是将LuaVM嵌入Nginx,让Nginx执行Lua脚本,但是高并发、非阻塞地处理各类恳求。Lua外置解释器可以挺好地将异步反弹转换成次序调用的方式。ngx_lua在Lua中进行的IO操作就会委托给Nginx的风波模型,进而实现非阻塞调用。开发者可以采用串行的形式编撰程序,ngx_lua会在进行阻塞的IO操作时手动中断,保存上下文,之后将IO操作委托给Nginx风波处理机制,在IO操作完成后,ngx_lua会恢复上下文,程序继续执行,这种操作对用户程序都是透明的。
每位Worker进程都持有一个Lua协程或LuaJIT实例,被这个Worker处理的所有恳求共享这个实例。每位恳求的context上下文会被Lua轻量级的解释器分隔,因而保证每位请求是独立的。
ngx_lua采用one-coroutine-per-request的处理模型,对于每位用户恳求,ngx_lua会唤起一个解释器用于执行用户代码处理恳求,当恳求处理完成后,这个解释器会被销毁。每位解释器都有一个独立的全局环境,承继于全局共享的、只读的公共数据。所以,被用户代码注入全局空间的任何变量都不会影响其他恳求的处理,但是这种变量在恳求处理完成后会被释放,这样就保证所有的用户代码都运行在一个sandbox(沙箱)中,这个沙箱与恳求具有相同的生命周期。
得益于Lua解释器的支持,ngx_lua在处理10000个并发恳求时,只须要极少的显存。依照测试linux启动nginx命令,ngx_lua处理每位恳求只须要2KB的显存红旗linux操作系统,假如使用LuaJIT还会更少。
NginxLua配置指令
ngx_lua定义的Nginx配置指令大致如下:
ngx_lua配置指令在Nginx的HTTP恳求处理阶段所处的位置如图:
常用配置指令
http {
...
#设置“.lua”外部库的搜索路径,此指令的上下文为http配置块
#";;"常用于表示原始的搜索路径
lua_package_path "/foo/bar/?.lua;/blah/?.lua;;";
lua_package_cpath "/usr/local/openresty/lualib/?/?.so;/usr/local/openresty/lualib/?.so;;";
}
对于以上两个指令,OpenResty可以在搜索路径中使用配准变量。比如,可以使用配准变量$prefix或${prefix}获取虚拟服务器server的前缀路径,server的前缀路径一般在Nginx服务器启动时通过-pPATH命令在指定。
格式为:init_by_lualua-script-str。
格式为:lua_code_cacheon|off
http {
...
#项目初始化
init_by_lua_file conf/luaScript/initial/loading_config.lua;
#调试模式,关闭lua脚本缓存
lua_code_cache on;
...
}
在缓存关掉的时,set_by_lua_file、content_by_lua_file、access_by_lua_file、content_by_lua_file等指令中引用的Lua脚本都将不会被缓存,所有的Lua脚本都将从头开始加载。
格式为:set_by_lua$destVarlua-script-strparams
location /set_by_lua_demo {
#set 指令定义两个Nginx变量
set $foo 1;
set $bar 2;
#调用Lua内联代码,将结果放入Nginx变量$sum
#Lua脚本的含义是,将两个输入参数$foo、$bar累积起来,然后相加的结果设置Nginx变量$sum中

set_by_lua $sum 'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])' $foo $bar;
echo "$foo + $bar = $sum";
}
运行结果:
work curl http://localhost/set_by_lua_demo
1 + 2 = 3
格式为:access_by_lua$destVarlua-script-str
location /access_demo {
access_by_lua 'ngx.log(ngx.DEBUG, "remote_addr = "..ngx.var.remote_addr);
if ngx.var.remote_addr == "192.168.56.121" then
return;
end
ngx.exit(ngx.HTTP_UNAUTHORIZED);
';
echo "hello world";
}
location /access_demo_2 {
allow "192.168.56.121";
deny all;
echo "hello world";
}
运行结果:
work curl http://localhost/access_demo
401 Authorization Required
401 Authorization Required
openresty/1.13.6.2
#上述案例运行日志:
2022/02/15 10:32:17 [debug] 26293#0: *17 [lua] access_by_lua(nginx-lua-demo.conf:85):1: remote_addr = 127.0.0.1
2022/02/15 10:32:17 [info] 26293#0: *17 kevent() reported that client 127.0.0.1 closed keepalive connection
work curl http://localhost/access_demo_2
403 Forbidden
403 Forbidden
openresty/1.13.6.2
#上述案例运行日志

2022/02/15 10:33:11 [error] 26293#0: *18 access forbidden by rule, client: 127.0.0.1, server: localhost, request: "GET /access_demo_2 HTTP/1.1", host: "localhost"
2022/02/15 10:33:11 [info] 26293#0: *18 kevent() reported that client 127.0.0.1 closed keepalive connection
格式为:content_by_lualua-script-str
location /errorLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an error log ");
ngx.say("错误日志调用成功");
';
}
location /infoLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an info log ");
ngx.say("业务日志调用成功");
';
}
location /debugLog {
content_by_lua '
ngx.log(ngx.ERR, "this is an debug log ");

ngx.say("调试日志调用成功");
';
}
OpenRestyv0.9.17版本之后,使用content_by_lua_block指令取代content_by_lua指令,防止对代码块中的字符串进行转译。
运行结果:
work curl http://localhost/errorLog
错误日志调用成功
work curl http://localhost/infoLog
业务日志调用成功
work curl http://localhost/debugLog
调试日志调用成功
NginxLua的外置常量和变量
外置变量
外置常量
外置常量基本是见名知意的,可以按照前面的实战案例,加深理解。
核心常量
HTTP方式常量
HTTP状态码常量
日志类型常量
Nginx+LUA基础到此结束,下一篇开始实战!并在实战中把握基础。