一、简介
OpenResty是一个基于Nginx与Lua的高性能Web平台,Nginx本身是支持Lua扩展的,但缺省情况下,Nginx的编译版本未包含Lua扩展,因此可将openresty视为自带有支持lua功能的扩展版本的nginx,所有nginx适用的东西,openresty也适用。这种“扩展”包含两层意思:
- 缺省增加了对Lua的支持
- 采用Lua开发了若干Nginx 模块
因此,OpenResty极大地丰富了Nginx的功能,甚至可以快速构造出高性能 Web 应用系统。由于lua与C固有的“亲和性”,引入lua,也就意味着引入了C。
OpenResty支持的lua模块见http://openresty.org/cn/components.html,它们是我们在lua脚本中可以引用的模块,它们当中既有OpenResty团队开发的,也有其它Nginx“爱好者”开发的,从上面页面的列表中,可以看出对MySQL、PostgreSQL、Memcached、Redis的连接被支持,摘要/加解密算法也被支持,需要注意的是,有些模块是需要底层C库的,也体现了上文所说的lua与C的“亲和性”。
本文介绍ubuntu22.04环境下openresty1.21.4.1的使用。另外,出于安全业务而采用Openresty的场景也不少,但本文未涉及此类应用。
二、半手工安装
虽然可以自动安装,但最好知道安装了些什么,因此需要了解一下手动安装。
openresty所有包在http://openresty.org/package/ubuntu/pool/main/o/下,如果只安装核心部分,需要4个包:
openresty-zlib(zilib压缩)
openresty-openssl111(安全通道)
openresty-pcre(perl正则表达式)
Openresty(核心包)
分别进入以上4个包的目录,选择对应ubuntu版本名(如22.04 为Jammy,20.04 为Focal,18.04为Bionic等)的deb文件,下载,然后运行
sudo dpkg –i XXX(包文件名)
进行安装,注意安装的顺序为上面列出的包顺序。
如果没有错误的话,openresty被安装到/usr/local/openresty下,可以打开这个目录看看。
三、启动
在启动之前,需要编写一个配置文件。openresty的配置文件与nginx是一致的,以下是官方文档中的配置文件示例(nginx.conf):
worker_processes 1;
error_log error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location / {
default_type text/html;
content_by_lua_block {
ngx.say("<p>hello, world</p>")
}
}
}
}
各行意义比较明显,可参考nginx的相关文档了解各行的意义。
Openresty建议将/usr/local/openresty/nginx/sbin加入PATH,因此可以写一个启动脚本(start.sh)来启动openresty(nginx),例如:
mypath=$(echo $PATH | grep -i /usr/local/openresty/nginx/sbin)
if test -z $mypath
then
PATH=/usr/local/openresty/nginx/sbin:$PATH
export PATH
fi
echo $PATH
nginx -p `pwd`/ -c nginx.conf
以上脚本适用于start.sh与nginx.conf在同一目录,否则修改上面脚本中最后一行。如果按上面nginx.conf中error_log文件的位置,应在此目录下建立logs子目录,否则会报错,大概是当error_log前没有目录时,会自动添加logs目录,而此时logs目录应事先存在。
三、Nginx 中lua处理流程

上图是OpenResty官网上在各个阶段lua脚本“可参与处理”的流程图,图中若干XX_by_lua可视为“lua块”,块中可以编写lua脚本,Openresty提供此脚本运行的“上下文环境”,通过这个预设的环境(若干接口函数),lua脚本可以与例如request,response等相关数据进行交互,进而定制Nginx的响应过程。
对于整个web响应流程,每个lua块所处的位置,以及它所能够处理的内容都是不同的,这也就是上图的意义。例如,上面content_by_lua*块的意义是web的应答内容由此块产生,而不会再转发了,可以在此块内编写response。Rewrite_by_lua*的意义是request内容可以被此块改写,再转发给后端。
关于各lua块的意义以及它可配置在哪个Nginx块,可参考官网https://openresty-reference.readthedocs.io/en/latest/Directives/,这个参考文档被称为“Directives(指令)”,是因为这些标记可以直接放到Nginx配置文件中。
四、Nginx中lua API
为便于开发,openresty提供了一系列api,可以获得web处理环节中的数据,但是由于环节不同,能够使用的api也不完全相同,也就是每个接口适用的场景不完全相同,有些可用于所有lua块,有些仅针对特定lua块,所以需要特别注意。
1.request内容相关
对于request,有ngx.req.*一系列api。
对uri,有ngx.req. get_uri_args /ngx. set_uri_args
对header, 有ngx.req.get_headers/ngx.req.set_headers或ngx.header.HEADER
对body,根据文档的,缺省是不读取的(大概是性能原因),虽然可设置lua_need_request_body来改变,但通常我们仅针对特定的uri才需要读取,此时先用ngx.req.read_body读取,然后再用ngx.req.get_body_data获得,这里采用的是将body全部读入内存的方法,因此可先设置读取的缓存区大小。OpenResty也提供了将body写入文件的方法。
如果要重构比较大的request body,可以使用ngx.req.init_body,ngx.req.append_body和ngx.req.finish_body。
2. response内容相关
OpenResty api对request的支持显然要比response好,可能“获得后端的response”这样的需求不多。
对header, 只有ngx.resp.get_headers,对于所有内容的捕获,可用ngx.location.capture/ngx.location.capture_multi,不过它们更像是web client的行为。
另外,body_filter_by_lua_block中,ngx.arg[1]是reponse body,这可能是唯一能够方便修改reponse内容的API。
如果不转发给后端应答,直接应答,有ngx.say/ngx.print。
3.跨块参数
有时候状态需要在lua块间转移,因此需要跨块参数,对于Nginx中定义的变量,采用ngx.var.VARIABLE获得,不过,建议采用ngx.ctx方式来定义和获取。ngx.ctx生命周期与request相同,因此ngx.ctx不能用于跨request来使用。如果需要跨request,可以考虑redis或文件进行缓存。
4.调试和日志
将有关信息写入日志是进行调试或运行维护的必要手段,将信息写入Nginx error.log的方法有ngx.log,此时最好在配置文件中设置日志的记录级别,例如
error_log error.log notice;
则日志级别等于或高于notice的才被写入日志。
关于可用的接口函数的意义及其适用于的lua块,可参考官网https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/。
五、引用lua文件
原则上,业务逻辑lua脚本可以嵌入到nginx.conf,如"启动”节中
content_by_lua_block { ngx.say("<p>hello, world</p>") }
但是如果逻辑比较复杂,还是将lua脚本单独放置较好,因此,一般情况下,需要将外部的lua脚本文件引入Nginx的配置文件。例如,将上面的nginx.conf改写为(相同部分略去):
…
http
{
…
lua_package_path "$prefix/lua/?.lua;/home/user/lua/?.lua;";
server{
…
location = /hello {
…
content_by_lua_block {
local hello = require "capture"
hello.greet("a Lua module")
}
}
}
}
其中关键的是lua_package_path(如果引用C动态库,采用lua_package_cpath)和require,它们其实与lua引用其它模块的用法是一致的:lua_package_path设置了lua中的package.path,require指出lua文件名,当系统遇到require时,搜索package.path指定的路径以载入该文件。关于lua系统搜索用户定义的模块的详细流程,比较复杂,可参阅lua官方文档。这里简单说,lua_package_path后面是以;分割的若干路径,其中?将被require后的名字替代(还有更复杂的规则),$prefix是当前的运行目录,因此,按上面的写法,系统将按如下顺序进行搜索:
{当前目录}/lua/capture.lua
/home/user/lua/capture.lua
如果找到,就将capture.lua载入。
capture.lua是一个符合lua要求的正常脚本,对于本例,它应该像这样:
local _M = {}
function _M.greet(name)
ngx.say("Host: ", ngx.req.get_headers()["Host"])
ngx.say("Greetings from ", name)
end
return _M
这里增加了一个读取request header内容的接口。这个示例的运行结果就是当浏览器请求/hello时,返回如下图所示结果。

本文介绍了在Ubuntu 22.04环境下,如何手动安装OpenResty 1.21.4.1,包括需要的四个核心包。讲解了OpenResty的启动、配置文件编写、Nginx中Lua处理流程以及提供的Lua API。此外,还提到了如何引用外部Lua文件并展示了Lua脚本示例,用于读取request header。
&spm=1001.2101.3001.5002&articleId=125658308&d=1&t=3&u=65488f7f46df498892f656aa1751bee1)
2133

被折叠的 条评论
为什么被折叠?



