openresty nginx 一文带你详解Nginx/OpenResty,Nginx Lua编程基础,学不会别怪我
openresty nginx 一文带你详解Nginx/OpenResty,Nginx Lua编程基础,学不会别怪我
Nginx Lua编程基础
OpenResty通过整合众多精心设计的Nginx插件(其中大部分由OpenResty团队独立研发)成功将Nginx转变为一个功能强大的通用Web服务平台。因此,Web开发者与系统架构师得以借助Lua脚本,灵活调用Nginx所提供的众多C和Lua模块,进而迅速搭建起能够应对高达10KB至1000KB级别单机并发连接的高效Web应用系统。
OpenResty旨在使Web服务能够在Nginx服务内部直接运行,充分挖掘Nginx非阻塞I/O模型的优势。它不仅对HTTP客户端的请求提供高效响应,而且对包括MySQL、PostgreSQL、Memcached以及Redis在内的远程后端,也能实现一致的高性能反馈。
实战案例说明
本节所涉及之配置文件,正是源自源码工程内部的nginx-lua-demo.conf文件。
在执行本节示例之前,务必对openresty-start.bat(或openrestystart.sh)脚本文件中的PROJECT_CONF变量进行更新,将其设定为nginx-luademo.conf配置文件,并随后对OpenResty进行重启操作。
Nginx Lua的执行原理
在OpenResty架构中,每个Worker进程都配备了一个Lua虚拟机。一旦请求被指派给某个Worker,便会在该虚拟机中启动一个协程。这些协程彼此之间数据是相互独立的,并且每个协程都拥有自己独立的全局变量。
ngx_lua将Lua编程语言集成于Nginx服务器中,使得Nginx能够执行Lua脚本,并能够高效地、无阻塞地应对各类网络请求。Lua语言自带的协程功能使得异步回调能够被转换成顺序执行的形式。在Lua中进行的所有I/O操作都交由Nginx的事件驱动模型来处理,确保了非阻塞式的调用方式。开发者可选择串联式编程模式,当ngx_lua遇到阻塞的I/O操作时,会自动暂停,保存当前环境状态,并将I/O任务转交给Nginx的事件处理系统处理。待I/O操作结束后,ngx_lua会恢复之前保存的环境状态,程序得以继续运行,整个过程对用户程序而言是无需感知的。
Nginx的每个Worker进程都配备了一个Lua解释器或LuaJIT的实例。该实例被用于处理所有由该Worker进程接收的请求。所有这些请求都共同使用这个单一的实例。在处理请求时,每个请求的上下文都会通过Lua的轻量级协程进行隔离,确保了各个请求之间的独立性。具体如图8-5所示。
图8-5 工作进程相互独立
每个工作进程都会启动一个LuaJIT虚拟机,且该进程内的所有协程可以共同使用这个虚拟机。
将Nginx的I/O基本操作进行封装处理,并将其引入到Lua虚拟机中,使得Lua脚本能够直接对其进行调用和操作。
每个外部请求都由一个Lua协程来负责处理,而这些协程在执行过程中彼此之间实现了数据上的独立分隔。
在执行Lua代码时,若调用涉及I/O操作的异步接口,则会暂停当前协程的执行(同时确保上下文数据的安全),但不会影响Worker进程的正常运行。
当I/O等异步操作得以顺利完成时,将恢复与协程相关的上下文数据,随后继续执行。
Nginx的每个Worker进程配备了一个Lua解释器或LuaJIT实例,该实例由该Worker负责处理的所有请求所共用。在这些请求中,每个请求的上下文都通过Lua的轻量级协程进行划分,确保了各个请求之间的独立性。ngx_lua运用了一种onecoroutine-per-request的处理机制,针对每一个用户请求,它会激活一个协程来执行用户代码以处理该请求。一旦请求得到处理,该协程便会终止。每个协程都配备了一个单独的全局环境,这个环境源自于一个全局共享的、不可修改的公共数据区域。因此,用户编写的代码若注入至全局空间中的变量,不会对其他请求的处理造成干扰,而且这些变量在请求处理完毕后会被自动清除,从而确保所有用户代码都在一个与请求生命周期一致的沙箱环境中运行。得益于Lua协程的功能,ngx_lua在应对10,000个并发请求时,所需的内存量极为有限。经过测试openresty nginx,ngx_lua在处理每个请求时仅消耗2KB的内存资源,而若采用LuaJIT,所需内存将进一步降低。因此,ngx_lua特别适合用来构建可扩展性和高并发性强的服务。
Nginx Lua的配置指令
ngx_lua提供了一套Nginx的配置指令,这些指令旨在规定用户Lua脚本运行的时机,以及确定如何输出Lua脚本的执行反馈。
ngx_lua定义的Nginx配置指令大致如表8-2所示。
表8-2 ngx_lua定义的Nginx配置指令
ngx_lua配置指令在Nginx服务器处理HTTP请求的过程中,其具体位置可参照图8-6进行了解。
图8-6展示了ngx_lua配置指令在Nginx服务器处理HTTP请求过程中的具体位置。
下面介绍Nginx Lua的常用配置指令。
(1)lua_package_path指令,它的格式如下:
设置Lua的包搜索路径为lua-style-path-str。
lua_package_path命令用于指定“.lua”格式的外部库文件的查找路径,该命令的使用场景是位于http配置部分。它的预设值是LUA_PATH环境变量中的内容openresty nginx 一文带你详解Nginx/OpenResty,Nginx Lua编程基础,学不会别怪我,或者是Lua编译时设定的默认路径。在lua-style-path-str字符串中,遵循了Lua路径的标准格式,其中“;;”符号通常用来标识原始的搜索路径。以下是一个示例:
在配置Lua扩展库的搜索路径时,请指定路径(默认为分号分隔的路径)。
设置Lua的包搜索路径为'/foo/bar/?.lua'和'/blah/?.lua',并以分号分隔。
OpenResty支持在搜索路径中应用变量替换。比如,通过使用变量$prefix或${prefix},可以提取出虚拟服务器server的前缀路径。这个前缀路径通常是由Nginx服务器在启动时,通过指定命令行选项-p PATH来设定的。
lua_package_cpath命令,其具体格式表现为:
Lua的包搜索路径设置为lua风格的cpath字符串。
lua_package_cpath命令用于指定Lua语言C模块所依赖的外部库文件在Linux系统中的".so"格式或在Windows系统中的".dll"格式的搜索路径,该指令的适用范围是http配置部分。其中,lua-style-cpath-str代表遵循Lua风格的cpath字符串,而“;;”符号通常用来代表原始的cpath设置。下面是一个简单的例子:
在C语言编写的Lua扩展模块的搜索路径进行配置(亦可用分号分隔)。
设置Lua的C路径为`/bar/baz/`目录下的所有`.so`文件,以及`/blah/blah/`目录下的所有`.so`文件,并以此结束配置。
同样,OpenResty支持在搜索路径“lua-style-cpath-str”中应用变量替换,例如,可以通过使用“$prefix”或“${prefix}”来提取服务器的前缀路径。
(3)init_by_lua指令,它的格式如下:
init_by_lua lua-script-str
init_by_lua指令仅适用于HTTP环境,并在配置文件加载期间执行。此时,Nginx的主进程正在处理Nginx配置文件的加载,而在整个全局Lua虚拟机层面上,会执行由参数lua-script-str指定的Lua脚本代码块。Nginx在接收到HUP信号后,会启动重新加载配置文件的过程,此时Lua虚拟机将被重新构建,而init_by_lua脚本将在新构建的虚拟机上重新执行。
当Lua脚本的缓存功能被禁用时,每接收到一次请求,都会触发init_by_lua处理程序的执行。可以通过设置lua_code_cache指令来关闭Lua脚本的缓存功能,而该缓存功能在默认情况下是开启状态。
在生产环境中,通常会启用Lua脚本缓存机制。当init_by_lua调用require函数加载模块文件时,这些文件会被存储在全局的Lua注册表package.loaded中。因此,在此处定义的任何全局变量或函数,都有可能对命名空间造成污染,进而影响系统性能。
(4)lua_code_cache指令,它的格式如下:
lua_code_cache on | off
lua_code_cache功能用于控制Lua脚本缓存的开启与关闭,此功能适用于http、server、location等配置块中。若缓存被关闭,每个请求将借助ngx_lua模块在独立的Lua虚拟机实例中执行。在缓存被关闭的情况下,对于set_by_lua_file、content_by_lua_file、access_by_lua_file等指令所调用的Lua脚本,均将失去缓存功能,需重新从源头进行加载。
借助这一指令,开发者能够高效地进行模型的快速编辑与更新,对代码进行修改后无需重新启动Nginx。
当缓存被关闭时,若在nginx.conf配置文件中嵌入Lua脚本,该脚本将不会自动重新加载。具体来说,诸如set_by_lua、content_by_lua、access_by_lua以及rewrite_by_lua所定义的Lua脚本块均不会进行反复更新,若Lua代码有所调整,则必须重新启动Nginx以使改动生效。
关闭缓存措施可能会对系统的整体运行效率造成不利影响。以禁用Lua脚本缓存为例,即便是执行一个基础的"hello world"Lua程序,其性能也可能出现显著降低,甚至可能降低至原来的十倍。
在生产环节中,绝对禁止关闭Lua脚本缓存,这一操作仅限于在开发阶段进行。
set_by_lua指令的格式如下:指定目标变量$destVar,执行Lua脚本字符串lua-script-str,并传递参数params。此指令的作用与rewrite模块的set指令相似,主要是将Lua脚本块执行后的返回值存入Nginx的变量中。set_by_lua指令在Nginx中的使用环境和执行时机与set指令大致相似。
以下示例展示了如何将Lua脚本中的加法运算结果赋值给Nginx的变量$sum,具体实现代码如下:
location /set_by_lua_demo {
#set指令定义两个Nginx变量
set $foo 1;
set $bar 2;
#调用内联代码,将结果放入Nginx变量$sum
使用Lua脚本设定值,计算参数一与参数二之和,并将结果赋值给变量$foo和$bar。
echo $sum;
}
在上述代码中,通过set_by_lua指令执行了一段Lua脚本,该脚本的功能相当简便,即对两个输入参数$a和$b进行累加操作,并将它们的和存储至Nginx的变量$sum中。
启动Nginx,访问
访问http://nginx.server/set_by_lua_demo?foo=bar这一地址,展示的输出效果与图8-7中所见一致。
图8-7中的set_by_lua指令,它利用Lua脚本为Nginx中的变量赋予特定的数值。
在应用set_by_lua配置指令时,您可以在Lua脚本之后附加一个参数列表。在Lua脚本内部,您能够利用Nginx Lua模块自带的ngx.arg表容器来获取具体的参数值。
access_by_lua指令的用法如下:通过指定$destVar变量。
lua-script-straccess_by_lua在HTTP请求处理的11个阶段中的access阶段执行,它通过Lua脚本实现对访问的管控。该指令在access阶段的最后部分运行,所以它总是在诸如allow和deny等指令之后执行,尽管这些指令同样位于access阶段。通常openresty nginx,我们可以利用access_by_lua功能进行深入的权限审查,该功能允许通过Lua脚本执行一系列繁杂的验证步骤,例如即时从数据库或其它后端系统中获取信息。
下面以一个基本案例为例,展示如何通过使用access_by_lua功能来为ngx_access模块添加IP地址筛选机制:
location /access_demo {
access_by_lua '
ngx.log(ngx.DEBUG, "远程地址为:", ngx.var.remote_addr);
若请求的远程地址等于192.168.233.128,则
return;
end
ngx执行退出操作,指定HTTP状态码为未授权(ngx.HTTP_UNAUTHORIZED)。
';
echo "hello world";
}
在上述代码中,允许通过的IP地址是192.168.233.128,该地址是作者电脑上虚拟的CentOS系统的IP。执行Nginx的重启操作后,在CentOS系统中使用curl命令访问/access_demo,展示的输出结果为:
在本地主机上执行命令行操作,通过curl工具访问指定的网络地址,即http://192.168.233.1,并请求资源access_demo。
hello world
若请求发起的IP地址并非192.168.233.128,则需利用ngx_lua模块中的Lua脚本函数ngx.exit来终止当前请求的处理过程,并立即向客户端发送401错误码,以此表明访问未被授权。若access_by_lua指令未能中断HTTP请求的处理流程,那么在access阶段之后,content阶段将得以顺畅进行,而echo指令的输出结果亦能成功发送至客户端。
(7)content_by_lua指令openresty nginx 一文带你详解Nginx/OpenResty,Nginx Lua编程基础,学不会别怪我,它的格式如下:
content_by_lua lua-script-str
content_by_lua指令专门用于配置在content阶段运行的Lua代码片段,其执行后生成的结果将直接构成响应内容。此指令适用于location上下文,并在content阶段进行执行。
在Nginx配置文件中,若要编写以lua-script-str形式的Lua脚本openresty nginx,需留意可能涉及特殊字符的转义问题。鉴于此,自OpenResty v0.9.17版本起,我们不建议继续使用该指令。取而代之,推荐使用content_by_lua_block指令。content_by_lua_block指令下的Lua代码块应通过花括号“{}”进行界定,而非采用字符串分隔符来区分。
至此,主要的Nginx Lua配置指令介绍完了。然而,上述内容仅对set_by_lua、access_by_lua、content_by_lua进行了阐述,并未提及set_by_lua_file、access_by_lua_file、content_by_lua_file等指令。这些后续指令与前面提到的指令在功能上保持一致,区别仅在于Lua脚本并非直接嵌入Nginx配置文件内,而是被放置在独立的脚本文件里。
Nginx Lua的内置常量和变量
Nginx Lua常用的内置变量如表8-3所示。
表8-3 Nginx Lua常用的内置变量
Nginx Lua常用的内置常量大致如表8-4所示。
表8-4 Nginx Lua常用的内置常量
本文旨在为大家深入剖析Nginx与OpenResty的细节,以及Nginx Lua编程的相关知识。在接下来的文章中,我们将继续探讨Nginx Lua编程的基础,并分享具体的编程实例。若您觉得这篇文章有价值,不妨将其转发并关注我们的公众号;我们在此对您的支持表示衷心的感谢!
- 随机文章
- 热门文章
- 热评文章