Openresty 是一个功能比较全面的应用服务器,它是基于标准的 nginx 为可以扩展很多第三方的模块,是一个中国人章亦春发起,web开发人员可以使用 lua脚本语言,对核心以及各种c模块进行编程,可以利用openresty快速搭建超过 1万并发高性能 web 应用系统。
这个openresty最早是雅虎中国的一个公司项目,基于Perl和Haskell实现,2007年开始开源,后来章亦春大佬加入淘宝后进行了彻底的设计和重写,这算二代openresty,一般称为ngx_openresty,基于nginx和lua进行开发
为啥叫这个名字,是因为最早为了顺应OpenAPI的潮流,后来基于ngx_openresty实现web服务和应用的意思,有了openrety,nginx不仅仅就是个代理,通过丰富的模块,可以成为功能全面的应用服务器了。
openresty用到了lua,lua又是什么?是一门简洁,优雅的编程语言,是1993年诞生于巴西的三位研究院手中,lua在葡萄牙语中意思是美丽的月亮。
lua可以嵌入其他应用程序且可以扩展的轻量级脚本语言,在很多领域例如游戏开发、分布式应用、图像处理等都有广泛的应用。
Nginx是一个主进程配合多个工作进程的工作模式,每个进程由单个线程来处理多个连接。
在生产环境中,我们往往会把cpu内核直接绑定到工作进程上,从而提升性能。
注意:Openresty的官网的文档介绍全部在 GitHua 根据module的跳转GithUb中
一、安装
安装方式分为两种,一种是预编译安装,一种是源码编译安装。
1.1 预编译安装
以CentOS举例 其他系统参照:http://openresty.org/cn/linux-packages.html
你可以在你的 CentOS 系统中添加 openresty 仓库,这样就可以便于未来安装或更新我们的软件包(通过 yum update 命令)。运行下面的命令就可以添加我们的仓库:
yum install yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
然后就可以像下面这样安装软件包,比如 openresty:
yum install openresty
如果你想安装命令行工具 resty,那么可以像下面这样安装 openresty-resty 包:
sudo yum install openresty-resty
1.2 源码编译安装
安装教程:http://openresty.org/en/installation.html
1.下载:
2.编译:
tar -zxvf openresty-1.19.9.1.tar.gz
3.编译
所需依赖依赖 gcc openssl-devel pcre-devel zlib-devel
安装:yum install gcc openssl-devel pcre-devel zlib-devel postgresql-devel
进入到目录cd /openresty-1.19.9.1.tar.gz
进行编译 ./configure --prefix=/usr/local/openresty/
您可以指定各种选项,比如
./configure --prefix=/opt/openresty \
--with-luajit \
--without-http_redis2_module \
--with-http_iconv_module \
--with-http_postgres_module
试着使用 ./configure --help 查看更多的选项。
4.安装
make && make install
5. 启动:
./nginx
6.停止
./nginx -s stop
7.检查配置文件是否正确
Nginx -t
8. 查看已安装模块和版本号
Nginx -V
二、Lua语言介绍
因为openresty是的二次开发是基于lua实现的,所以需要了解lua的基本使用,可以查看这篇文章。
https://blog.csdn.net/weixin_52834606/article/details/129472732?spm=1001.2014.3001.5501
三、目录介绍

luajit :是lua 脚本的编译器目录,内嵌了lua环境
lualib:一些lua的第三方库
nginx:是一个nginx服务器
四、lua-nginx-module
模块的参数和API配置:https://github.com/openresty/lua-nginx-module
ngx_http_lua_module - 将 Lua 的强大功能嵌入到 Nginx HTTP 服务器中。
该模块是OpenResty的核心组件。如果您正在使用这个模块,那么您实际上是在使用 OpenResty。
此模块未随 Nginx 源代码一起分发。请参阅 安装说明。
这是 OpenResty 的核心组件。如果你正在使用这个模块,那么你实际上是在使用 OpenResty :)
这个模块主要两大类操作的内容,1.操作的指令,2.操作的的API
4.1 简单使用
指令官方文档:https://openresty-reference.readthedocs.io/en
在nginx的配置文件可以进行写lua的脚本的功能,也可以引入lua脚本。
在Nginx.conf 中写入
location /lua {
# 告知浏览器使用 text/html解析
default_type text/html;
# 告知这里是 lua内存
content_by_lua '
# 表示输出
ngx.say("<p>Hello, World!</p>")
';
}
4.2 引入方式
这种方式的特点就是将使用 lua的内容分离出去,创建一个 lua.conf的配置文件,通过 nginx的 include引入的方式。
1.创建一个 lua.conf文件,用来写有关引用lua的代码
server {
listen 80;
server_name localhost;
location /lua {
default_type text/html;
# 使用引入lua的文件的方式
content_by_lua_file conf/lua/hello.lua;
}
}
2.创建lua的文件
ngx.say("<p>hello word ! </p>")
3.在nginx.conf引入创建的lua.conf文件·
include /conf/lua.conf
4.3 指令配置
官网地址:http://openresty.org/cn/components.html
这些是指令Openresty在nginx.conf的相关指令。
使用 Lua 编写 Nginx 脚本的基本构建块是指令。指令用于指定何时运行用户 Lua 代码以及如何使用结果。下图显示了指令执行的顺序。

以下讲解一下常用的一些指令,具体可以看官网文档:
lua_code_cache
语法:lua_code_cache on | off
默认值:lua_code_cache on
适用于:http、server、location 、location if
作用:对·*_by_lua_file相关指令引入的lua文件进行热部署,在关闭之后将不在缓存引入的文件,可以达到热部署的功能,对性能有影响,在线上环境不要开启。
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。Content-Type如果不需要 Lua 请求处理程序的默认响应标头,请停用此指令。
init_by_lua
语法:init_by_lua <lua-script-str>
默认值:无·
适用于:http
阶段:加载配置
作用:在加载时可以执行端Nginx字符串中的Lua代码,在发布之后不建议使用建议使用下面这个。
http {
init_by_lua '
print("I need no extra escaping here, for example: \r\nblah")
'
}
init_by_lua_block
语法:init_by_lua_block {lua-script}
默认值:无·
适用于:http
阶段:加载配置 这个需要 lua-nginx-module 的版本需要是 0.9.17
作用:可以把他当时java 的 静态代码块在加载时执行,并且如果关闭了缓存之后,每次请求会重新执行,可以在进行初始化操作。当 Nginx 接收到HUP信号并开始重新加载配置文件时,Lua 虚拟机也将重新创建并init_by_lua_block在新的 Lua 虚拟机上再次运行。如果lua_code_cache指令被关闭(默认开启),init_by_lua_block处理程序将在每次请求时运行,因为在这种特殊模式下,总是为每个请求创建一个独立的 Lua VM。
lua_shared_dict dogs 1m;
# 您还可以在此阶段初始化lua_shared_dict shm 存储。这是一个例子:
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"))
}
}
}
init_by_lua_file
语法:init_by_lua_file <lua-path>
默认值:无·
适用于:http
阶段:加载配置 等同于init_by_lua_block,只是指定的文件包含要执行的<path-to-lua-script-file>Lua 代码。
set_by_lua
语法:set_by_lua $res <lua-script-str> [$arg1 $arg2 ...]
默认值:无·
适用于:server、server if 、location 、location if
作用:修改nginx的变量。
set_by_lua_block
语法:set_by_lua $res <lua-script-str> [$arg1 $arg2 ...]
默认值:无·
适用于:server、server if 、location 、location if
作用:修改或声明nginx的变量。set_by_lua_block在lua的代码块中可以返回一个值作为值,也可以在里面修改。
location /foo {
set $diff ''; # we have to predefine the $diff variable here
# 返回是 $sum 也就是的 a + b =88
set_by_lua_block $sum {
local a = 32
local b = 56
ngx.var.diff = a - b -- write to $diff directly
return a + b -- return the $sum value normally
}
echo "sum = $sum, diff = $diff";
}
content_by_lau
语法:content_by_lua <lua-script-str>
默认值:无·
适用于:location 、location if
阶段:内容
作用:可以在里面直接编写lua脚本命令。
content_by_lua_block
语法:content_by_lua_block {lua-script}
默认值:无·
适用于:location 、location if
阶段:内容
作用:可以在里面直接编写lua脚本。Lua 代码可以进行API 调用,并在独立的全局环境(即沙箱)中作为新生成的协程执行。
content_by_lua_file
语法:content_by_lua_file <file-path>
默认值:无·
适用于:location 、location if
阶段:内容
作用:通过引入的方式引入脚本文件。
server_rewite_by_lua_block
语法:server_rewrite_by_lua_block {lua-script}
适用于:http、server
阶段:服务器重写
作用·:用于在请求被 Nginx 处理之前,对请求进行 Lua 代码块的处理。具体而言,它允许您在接收到请求的服务器级别(即 server 块内)执行 Lua 代码块,以修改请求 URI 或添加额外的请求头、请求体等信息。这个指令可以让开发者通过编写 Lua 代码块,动态地修改请求 URI,甚至可以通过 Lua 调用其他的服务或者修改请求头和请求体等内容。
注意:需要注意的是,使用 server_rewrite_by_lua_block 指令会对性能产生一定的影响,因此应谨慎使用。
rewrite_by_lua
语法:rewrite_by_lua <lua-script-str>
适用于:http、server、location、location if
阶段:重写尾部
作用:在 nginx 的 rewrite 阶段执行,可以进行请求 URI 的重写,例如将 URI 的路径替换为指定字符串,重定向等。常见的应用场景包括 URL 美化和 RESTful API 的实现.
location /old-url {
rewrite_by_lua '
ngx.redirect("/foo")
';
}
access_by_lua
访问控制
header_filter_by_lua
修改响应头
boy_filter_by_lua
修改响应体
log_by_lua
日志
4.4 Nginx的API
https://github.com/openresty/lua-nginx-module/blob/master/README.markdown#introduction
各种*_by_lua,*_by_lua_block和*_by_lua_file配置指令充当nginx.conf文件中 Lua API 的网关。下面描述的 Nginx Lua API 只能在这些配置指令的上下文中运行的用户 Lua 代码中调用。
API 以两个标准包ngx和ndk. 这些包在 ngx_lua 中的默认全局范围内,并且在 ngx_lua 指令中始终可用。
这些包可以像这样引入到外部 Lua 模块中:
ngx.arg
语法:val = ngx.arg[index]
适用于:set_by_lua* , body_filter_by_lua*
作用:取出nginx的变量,通常用于处理 Nginx 的输出缓冲区中的数据。
location /foo {
set $a 32;
set $b 56;
set_by_lua $sum
'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])'
$a $b;
echo $sum;
}
ngx.var.VARIABLE
语法:ngx.var.VAR_NAME
适用于:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*
作用:读取Nginx变量,还可以写入nginx变量,写入变量时必须是Nginx已经存在变量。具体看官网介绍:https://github.com/openresty/lua-nginx-module#ngxarg
value = ngx.var.some_nginx_variable_name
ngx.var.some_nginx_variable_name = value
# 请注意,只能将已定义的Nginx变量写入. 例如:_
location /foo {
set $my_var ''; # this line is required to create $my_var at config time
content_by_lua_block {
ngx.var.my_var = 123
...
}
}
location /var {
default_type text/html;
content_by_lua_block {
local uri = ngx.var.request_uri
ngx.say('uri:',uri)
}
}
核心变量
适用于:init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, *log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*
作用:可以作为Lua的常量使用·如下:
ngx.OK (0)
ngx.ERROR (-1)
ngx.AGAIN (-2)
ngx.DONE (-4)
ngx.DECLINED (-5)
请注意,只有三个常量被用于Lua 的 Nginx API使用(即,ngx.exit接受ngx.OK, ngx.ERROR, 和ngx.DECLINED作为输入)。
if some_condition then
return ngx.OK
else
return ngx.ERROR
end
HTTP 方法常量
适用于: init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*
作用:标志性的请求的方式,可以使用在ngx.location.capture函数的一个参数,表示使用 HTTP 方法进行发送子请求。
这些常量通常用于ngx.location.capture和ngx.location.capture_multi方法调用。
ngx.HTTP_GET
ngx.HTTP_HEAD
ngx.HTTP_PUT
ngx.HTTP_POST
ngx.HTTP_DELETE
ngx.HTTP_OPTIONS (added in the v0.5.0rc24 release)
ngx.HTTP_MKCOL (added in the v0.8.2 release)
ngx.HTTP_COPY (added in the v0.8.2 release)
ngx.HTTP_MOVE (added in the v0.8.2 release)
ngx.HTTP_PROPFIND (added in the v0.8.2 release)
ngx.HTTP_PROPPATCH (added in the v0.8.2 release)
ngx.HTTP_LOCK (added in the v0.8.2 release)
ngx.HTTP_UNLOCK (added in the v0.8.2 release)
ngx.HTTP_PATCH (added in the v0.8.2 release)
ngx.HTTP_TRACE (added in the v0.8.2 release)
HTTP 状态常量
适用于 init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, ssl_client_hello_by_lua*
作用:常用于表示继续处理当前的请求,即让Nginx继续执行它的请求处理流程。根据返回的状态码来进行。
value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release)
value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release)
value = ngx.HTTP_OK (200)
value = ngx.HTTP_CREATED (201)
value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release)
value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release)
value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release)
value = ngx.HTTP_SPECIAL_RESPONSE (300)
value = ngx.HTTP_MOVED_PERMANENTLY (301)
value = ngx.HTTP_MOVED_TEMPORARILY (302)
value = ngx.HTTP_SEE_OTHER (303)
value = ngx.HTTP_NOT_MODIFIED (304)
value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release)
value = ngx.HTTP_PERMANENT_REDIRECT (308)
value = ngx.HTTP_BAD_REQUEST (400)
value = ngx.HTTP_UNAUTHORIZED (401)
value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release)
value = ngx.HTTP_FORBIDDEN (403)
value = ngx.HTTP_NOT_FOUND (404)
value = ngx.HTTP_NOT_ALLOWED (405)
value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release)
value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release)
value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release)
value = ngx.HTTP_GONE (410)
value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release)
value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release)
value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release)
value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release)
value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)
value = ngx.HTTP_NOT_IMPLEMENTED (501)
value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) (kept for compatibility)
value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release)
value = ngx.HTTP_SERVICE_UNAVAILABLE (503)
value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)
value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release)
value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)
实例:
location /foo {
content_by_lua_block {
ngx.say("foo")
return ngx.HTTP_CONTINUE
}
}
location /bar {
content_by_lua_block {
ngx.say("bar")
}
}
当请求/foo时,该location处理完成后,返回ngx.HTTP_CONTINUE,Nginx会继续遍历后续的location,
即/bar。在/bar中处理完成后,请求处理完成并返回。因此,对于请求/foo,最终会输出foo和bar。
Nginx 日志级别常量
适用于:init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*
作用:控制 Nginx 记录日志的详细程度,这些常量通常由ngx.log方法使用。
ngx.DEBUG: 调试信息级别,最详细的日志级别。
ngx.INFO: 信息级别,记录正常的操作和状态。
ngx.NOTICE: 注意级别,记录需要注意的情况。
ngx.WARN: 警告级别,记录非致命错误和异常情况。
ngx.ERR: 错误级别,记录致命错误和异常情况。
ngx.CRIT: 严重错误级别,记录严重错误和崩溃情况。
ngx.ALERT: 警戒级别,记录需要立即采取行动的情况。
ngx.EMERG: 紧急级别,最高级别,记录系统不可用的情况。
ngx.STDERR
可以在 Nginx 配置文件中通过 error_log 指令设置日志级别,如:
error_log logs/error.log ngx.ERR;
此配置表示记录所有 ERR 级别的日志信息到 logs/error.log 文件中。在 OpenResty 中,可以使用 Nginx 的日志 API,如 ngx.log() 函数,输出指定级别的日志信息。例如:
ngx.log(ngx.ERR, "an error occurred: ", err)
print
作用:打印输出日志信息。
适用于: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*
ngx.NOTICE 值是会将值写入到 error.log文件中。
ngx.cxt
适用于:init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, exit_worker_by_lua*
作用:可用于存储每个请求的Lua上下文数据,并且具有与当前请求相同的生命周期(与Nginx变量一样)。请求之间数据不互通,并且每次请求结束值都会消失。
在请求的生命周期中都是存在的:
也就是说,ngx.ctx.foo条目在请求的重写、访问和内容阶段持续存在。
location /test {
rewrite_by_lua_block {
ngx.ctx.foo = 76
}
access_by_lua_block {
ngx.ctx.foo = ngx.ctx.foo + 3
}
content_by_lua_block {
ngx.say(ngx.ctx.foo)
}
}
ngx.location.capture
语法:res = ngx.location.capture(uri,options?)
适用于:rewrite_by_lua* 、access_by_lua*、content_by_lua*
作用:用于内部Lua的API的请求发送。
注意:使用发出同步请求但是发送Nginx的子请求是非堵塞的。
返回结果:
res = ngx.location.capture(uri)
返回具有 4 个槽的 Lua 表:res.status、res.header、res.body和res.truncated。
res.status保存子请求响应的响应状态代码。
res.body 是请求的响应体
res.truncated 是用来检测响应体是否被修改过。
res.header包含子请求的所有响应头,它是一个普通的 Lua 表。对于多值响应标头,该值是一个 Lua(数组)表,它按照它们出现的顺序保存所有值。例如,如果子请求响应标头包含以下行:
Set-Cookie: a=3
Set-Cookie: foo=bar
Set-Cookie: baz=blah
请求参数可选项:
method 指定子请求的请求方法,它只接受像 . 这样的常量ngx.HTTP_POST。
body 指定子请求的请求主体(仅限字符串值)。
args 指定子请求的 URI 查询参数(接受字符串值和 Lua 表)
vars 采用一个 Lua 表,其中包含用于将子请求中指定的 Nginx 变量设置为该选项值的值。此选项在发行版中首次引入v0.3.1rc31。
copy_all_vars 指定是否将当前请求的所有 Nginx 变量值复制到相关子请求。子请求中 Nginx 变量的修改不会影响当前(父)请求。此选项在发行版中首次引入v0.3.1rc31。
share_all_vars 指定是否与当前(父)请求共享子请求的所有 Nginx 变量。子请求中 Nginx 变量的修改将影响当前(父)请求。启用此选项可能会由于不良的副作用而导致难以调试的问题,并且被认为是有害的。仅当您完全知道自己在做什么时才启用此选项。
always_forward_bodybody当设置为 true 时,如果未指定 该选项,则当前(父)请求的请求主体将始终转发到正在创建的子请求。ngx.req.read_body()或lua_need_request_body读取的请求体将直接转发给子请求,而不会在创建子请求时复制整个请求体数据(无论请求体数据缓存在内存缓冲区还是临时文件中) . 默认情况下,该选项是,false当body未指定该选项时,只有当子请求采用 orPUT请求POST方法时,才会转发当前(父)请求的请求体。
location /capture {
default_type text/html;
content_by_lua_block {
ngx.say("start-----");
local res = ngx.location.capture("/var")
ngx.print("xxxxxxxxxxxxxx");
ngx.say(res.status,res.body,res.truncated)
}
}
ngx.location.capture_multi
语法: res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })
适用于: rewrite_by_lua*、access_by_lua*、content_by_lua*
作用:就像ngx.location.capture一样,但支持并行运行的多个子请求。在所有子请求终止之前,此函数不会返回。总延迟是单个子请求的最长延迟而不是总和。
res1, res2, res3 = ngx.location.capture_multi{
{ "/foo", { args = "a=3&b=4" } },
{ "/bar" },
{ "/baz", { method = ngx.HTTP_POST, body = "hello" } },
}
if res1.status == ngx.HTTP_OK then
...
end
if res2.body == "BLAH" then
...
end
ngx.re.sub
语法:newstr, n, err = ngx.re.sub(subject, regex, replace, options?)
适用于:init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*
作用·:用于通过正则表达式进行字符串替换操作。
newstr, n, err = ngx.re.sub(subject, regex, replace, options?)
其中:
subject:要进行替换的字符串。
regex:用于匹配的正则表达式。
replace:用于替换的字符串,可以包含正则表达式捕获组,例如 "$1-$2"。
options:一个可选的表,用于设置正则表达式的选项,如 i 表示不区分大小写匹配,j 表示开启 PCRE-JIT 编译等。
该函数返回替换后的新字符串 newstr,替换次数 n,以及可能的错误信息 err。
ngx.req.get_headers
语法:headers, err = ngx.req.get_headers(max_headers?, raw?)
适用于:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
作用:获取所有的请求信息。
-- 获取当前请求的请求头
local headers ,err = ngx.req.get_headers();
-- 表示报错
if err == 'truncated' then
ngx.say("请求头获取失败!");
else
ngx.say("Host : ", headers["Host"], "<br/>")
ngx.say("user-agent : ", headers["user-agent"], "<br/>")
ngx.say("user-agent : ", headers.user_agent, "<br/>")
for k,v in pairs(headers) do
-- 判断v 是否一个table如果是则进行,格式连接
if type(v) == "table" then
ngx.say(k.." : ".. table.concat(v,",")..'<br/>');
else
ngx.say(k.." : "..v.."<br/>");
end
end
end
ngx.req.get_uri_agrs
语法: args, err = ngx.req.get_uri_args(max_args?, tab?)
适用于:set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*
作用:返回请求中get的参数。
location = /test {
content_by_lua_block {
local args, err = ngx.req.get_uri_args()
if err == "truncated" then
-- one can choose to ignore or reject the current request here
end
for key, val in pairs(args) do
if type(val) == "table" then
ngx.say(key, ": ", table.concat(val, ", "))
else
ngx.say(key, ": ", val)
end
end
}
}
ngx.req.read_body
语法:ngx.req.read_body
适用于:rewrite_by_lua*、access_by_lua*、content_by_lua*
作用:将当前的请求体流读取,注意不会重复读取,并且将读取的数据保存缓存区或者磁盘文件中。
ngx.req.read_body()
local args = ngx.req.get_post_args()
ngx.req.get_post_args
语法:args, err = ngx.req.get_post_args(max_args?)
适用于: rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
作用:获取当前post请求的请求体的参数,需要先通过ngx.req.read_body()获取请求体流。
location = /test {
content_by_lua_block {
ngx.req.read_body()
local args, err = ngx.req.get_post_args()
if err == "truncated" then
-- one can choose to ignore or reject the current request here
end
if not args then
ngx.say("failed to get post args: ", err)
return
end
for key, val in pairs(args) do
if type(val) == "table" then
ngx.say(key, ": ", table.concat(val, ", "))
else
ngx.say(key, ": ", val)
end
end
}
}
其他具体看官网··....
4.5 案例测试
获取nginx的请求的所有args
local uri_args = ngx.req.get_uri_args()
for k, v in pairs(uri_args) do
if type(v) == "table" then
ngx.say(k, " : ", table.concat(v, ", "), "<br/>")
else
ngx.say(k, ": ", v, "<br/>")
end
end
获取nginx请求的信息
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by 千城丶Y.
--- DateTime: 2023/3/11 22:25
---
-- 获取当前请求的请求头
local headers ,err = ngx.req.get_headers();
-- 表示报错
if err == 'truncated' then
ngx.say("请求头获取失败!");
else
ngx.say("Host : ", headers["Host"], "<br/>")
ngx.say("user-agent : ", headers["user-agent"], "<br/>")
ngx.say("user-agent : ", headers.user_agent, "<br/>")
for k,v in pairs(headers) do
-- 判断v 是否一个table如果是则进行,格式连接
if type(v) == "table" then
ngx.say(k.." : ".. table.concat(v,",")..'<br/>');
else
ngx.say(k.." : "..v.."<br/>");
end
end
end
获取post请求参数
ngx.req.read_body()
ngx.say("post args begin", "<br/>")
local post_args = ngx.req.get_post_args()
for k, v in pairs(post_args) do
if type(v) == "table" then
ngx.say(k, " : ", table.concat(v, ", "), "<br/>")
else
ngx.say(k, ": ", v, "<br/>")
end
end
http协议版本
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")
请求方法
ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")
原始的请求头内容
ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>")
body内容体数据
ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")
五、nginx的openresty缓存方式
默认基于nginx的话也是有一些缓存的方式比如redis的缓存 或者是静态资源的缓存等等一些系列的方式。在openresty中也是提供了缓存的解决方案。
5.1 共享字典缓存
https://github.com/openresty/lua-nginx-module#ngxshareddict
声明:
语法:lua_shared_dict <name><size>
适用于:http
作用:声明一个共享内存区域,<name>作为基于 shm 的 Lua 字典的存储ngx.shared.<name>。
共享内存区域始终由当前 Nginx 服务器实例中的所有 Nginx 工作进程共享。
该<size>参数接受大小单位,例如k和m:
http {
lua_shared_dict dogs 10m;
...
}
使用方式:
语法: dict = ngx.shared.DICT
语法: dict = ngx.shared[name_var]
适用于: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*, ssl_client_hello_by_lua*
并且,该shared提供了对声明的字典的操作命令,具体可以看官网介绍。
共享字典,效率比较,但是因为是多线程共享,一致性的保证就会出现锁的机制,这个是能被所有访问,并且保证原子性的。
这个是通过nginx内存做缓存,可以做上游服务器的缓存数据了。
shared_data的缺陷就是因为要保证原子性,在多线程中就会有锁的产生,会影响qps的效率。
local data = ngx.shared.shared_data ;
local i = data:get("i");
if not i then
i = 10;
data:set("i",i);
ngx.say("i值:"..data:get("i"))
else
data:incr("i",10);
ngx.say("i值:"..data:get("i"))
end
5.2 lua-resty-lrucache
Lua 实现的一个简单的 LRU 缓存,适合在 Lua 空间里直接缓存较为复杂的 Lua 数据结构:它相比 ngx_lua 共享内存字典可以省去较昂贵的序列化操作,相比 memcached 这样的外部服务又能省去较昂贵的 socket 操作
https://github.com/openresty/lua-resty-lrucache
这个库为 OpenResty和 ngx_lua模块实现了一个简单的 LRU 缓存。此缓存还支持过期时间。
该库已经集成openresty里,使用时需要在lua脚本中引入既可
local lrucache = require "resty.lrucache"
他也是可以在nginx中使用缓存,它是完全通过lua实现的, 它是在多个独立进程去运行,多个进程同时运行时可能会有内存的浪费,独立进程执行,单线程中 就不需要在做一些修改操作时再加锁了,性能会高些,它可以做 LRU 算法的清理工作。
它的存储的概念和shared不一样,它是以key value的个数为限制, 而shared则是以内存大小,shared可以很好的控制好内存大小。而这个则无法很好的预估占用内存,这样的好处就是内存的弹性化,不好出现内存写满了内容写不进去。
注意:使用lrucache不能关闭lua_code_cache模块,因为关闭缓存之后每次请求会重新去编译加载,导致缓存失效就会缓存失效。
因为lua语言是动态语言,它基于静态编译和静态脚本之间就是他执行完一次后会有一层缓存。
需要将初始化的对象变成常驻对象。
一下错误是因为lua的requrie没有找到文件,它默认是从以下路径去找文件,没有找到,所需要需要把通过requrie导入的文件到指定路径下或者修改指定目录。/usr/local/openresty2/lualib

默认会在这些目录下去找文件
location /lru {
default_type text/html;
content_by_lua_block {
local obj = require("lua/LRUCache")
obj.go();
}
}
local _M = {}
-- alternatively: local lrucache = require "resty.lrucache.pureffi"
local lrucache = require "resty.lrucache"
-- we need to initialize the cache on the lua module level so that
-- it can be shared by all the requests served by each nginx worker process:
local c, err = lrucache.new(200) -- allow up to 200 items in the cache
if not c then
error("failed to create the cache: " .. (err or "unknown"))
end
function _M.go()
c:set("dog", 32)
c:set("cat", 56)
ngx.say("dog: ", c:get("dog"))
ngx.say("cat: ", c:get("cat"))
c:set("dog", { age = 10 }, 0.1) -- expire in 0.1 sec
c:delete("dog")
c:flush_all() -- flush all the cached data
end
return _M
性能差距不大,区别一个有锁一个没有锁 。
六、lua-resty-redis
这个库可以让nginx直接通过lua操作reids数据库。
官网:https://github.com/openresty/lua-resty-redis
这个 Lua 库是 ngx_lua nginx 模块的 Redis 客户端驱动程序:
https://github.com/openresty/lua-nginx-module/#readme
这个 Lua 库利用了 ngx_lua 的 cosocket API,它确保了 100% 的非阻塞行为。
请注意,至少需要ngx_lua 0.5.14或OpenResty 1.2.1.14 。
注意:
一般写的操作都不会由nginx的操作,除非一些简单的,如计数操作,因为如在nginx修改比较复杂的逻辑,那么就无法保证redis的一致性,这一点需要注意,要保证缓存和持久化的数据的一致性。
6.1 常用方法
local res, err = red:get("key")
local res, err = red:lrange("nokey", 0, 1)
# 通过cjson 将结果以json的格式展示
ngx.say("res:",cjson.encode(res))
6.2 创建连接
red, err = redis:new()
ok, err = red:connect(host, port, options_table?)
6.3 连接超时时间
red:set_timeout(time)
6.4 keepalive
red:set_keepalive(max_idle_timeout, pool_size)
6.3 close
ok, err = red:close()
6.6 init_pipeline
启用 redis 流水线模式。commit_pipeline对 Redis 命令方法的所有后续调用将自动缓存,并在调用该方法或通过调用该方法取消时一次性发送到服务器cancel_pipeline。
这种方法总是成功的。
如果 redis 对象已经处于 Redis 流水线模式,则调用此方法将丢弃现有缓存的 Redis 查询。
可选n参数指定要添加到此管道的命令的(近似)数量,这可以使事情变得更快一些。
red:init_pipeline()
results, err = red:commit_pipeline()
6.7 认证
local res, err = red:auth("foobared")
if not res then
ngx.say("failed to authenticate: ", err)
return
end
6.8 案例
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
ok, err = red:set("dog", "an animal")
if not ok then
ngx.say("failed to set dog: ", err)
return
end
ngx.say("set result: ", ok)
local res, err = red:get("dog")
if not res then
ngx.say("failed to get dog: ", err)
return
end
if res == ngx.null then
ngx.say("dog not found.")
return
end
ngx.say("dog: ", res)
6.9 redis-cluster
redis的集群部署连接可以使用该模块
https://github.com/steve0511/resty-redis-cluster
七、lua-resty-mysql
https://github.com/openresty/lua-resty-mysql
具体查看官网介绍
注意:
如果要在nginx中使用mysql要注意,最好不要有带参数的查询,因为他没有像java那样有sql预编译后在给mysql执行,可能会出现sql注入。
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("failed to instantiate mysql: ", err)
return
end
db:set_timeout(1000) -- 1 sec
local ok, err, errcode, sqlstate = db:connect{
host = "192.168.44.211",
port = 3306,
database = "zhangmen",
user = "root",
password = "111111",
charset = "utf8",
max_packet_size = 1024 * 1024,
}
ngx.say("connected to mysql.<br>")
local res, err, errcode, sqlstate = db:query("drop table if exists cats")
if not res then
ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
res, err, errcode, sqlstate =
db:query("create table cats "
.. "(id serial primary key, "
.. "name varchar(5))")
if not res then
ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
ngx.say("table cats created.")
res, err, errcode, sqlstate =
db:query("select * from t_emp")
if not res then
ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))
local ok, err = db:set_keepalive(10000, 100)
if not ok then
ngx.say("failed to set keepalive: ", err)
return
end
八、lua-restry-template 模板渲染
ua-resty-template是用于 Lua 和 OpenResty 的编译 (1) (HTML) 模板引擎。
它可以分为两块,一是要渲染的数据、二是html静态页面格式。
如果学习过JavaEE中的servlet和JSP的话,应该知道JSP模板最终会被翻译成Servlet来执行;
而lua-resty-template模板引擎可以认为是JSP,其最终会被翻译成Lua代码,然后通过ngx.print输出。
lua-resty-template大体内容有:
l 模板位置:从哪里查找模板;
l 变量输出/转义:变量值输出;
l 代码片段:执行代码片段,完成如if/else、for等复杂逻辑,调用对象函数/方法;
l 注释:解释代码片段含义;
l include:包含另一个模板片段;
l 其他:lua-resty-template还提供了不需要解析片段、简单布局、可复用的代码块、宏指令等支持。
基础语法
l {(include_file)}:包含另一个模板文件;
l {* var *}:变量输出;
l {{ var }}:变量转义输出;
l {% code %}:代码片段;
l {# comment #}:注释;
l {-raw-}:中间的内容不会解析,作为纯文本输出;
这个模块默认是没有的,需要单独引入。
8.1 引入模板
1.下载
2.解压
tar -zxvf lua-resty-template-1.9.tar.gz
3.模块导入
将解压好的文件进行放到 xx/openresty/lualib下
cp -r /tools/lua-resty-template-1.9 /usr/local/openresty2/lualib/
将lua-resty-template-1.9/lib/resty目录下的 template template.lua移动到xxx//lualib/resty/下

8.2 模板存放位置
nginx.conf中配置
set $template_root /usr/local/openresty2/nginx/tpl;
8.3 测试
这个模板是两个组合一个是展示静态的一个逻辑处理动态。
location /tpl {
# 设置template的文件路径
set $template_root /usr/local/openresty2/nginx/tpl;
content_by_lua_file lua/view.lua;
}
逻辑
local template = require "resty.template"
local view = template.new "view.html"
view.message = "Hello, World!"
view:render()
内容展示
<!DOCTYPE html>
<html>
<body>
<h1>{{message}}</h1>
</body>
</html>
8.4 执行函数,得到渲染之后的内容
local func = template.compile("view.html")
local content = func(context)
ngx.say("xx:",content)
8.5 resty.template.html
local template = require("resty.template")
local html = require "resty.template.html"
template.render([[
<ul>
{% for _, person in ipairs(context) do %}
{*html.li(person.name)*} --
{% end %}
</ul>
<table>
{% for _, person in ipairs(context) do %}
<tr data-sort="{{(person.name or ""):lower()}}">
{*html.td{ id = person.id }(person.name)*}
</tr>
{% end %}
</table>]], {
{ id = 1, name = "Emma"},
{ id = 2, name = "James" },
{ id = 3, name = "Nicholas" },
{ id = 4 }
})
<!DOCTYPE html>
<html>
<body>
<h1>{{message}}</h1>
</body>
</html>
8.6 多值传入
template.caching(false)
local template = require("resty.template")
local context = {
name = "lucy",
age = 50,
}
template.render("view.html", context)
<!DOCTYPE html>
<html>
<body>
<h1>name:{{name}}</h1>
<h1>age:{{age}}</h1>
</body>
</html>
8.7 模板管理与缓存
模板缓存:默认开启,开发环境可以手动关闭
template.caching(true)
模板文件需要业务系统更新与维护,当模板文件更新后,可以通过模板版本号或消息通知Openresty清空缓存重载模板到内存中
template.cache = {}
8.8 完整页面
逻辑
local template = require("resty.template")
template.caching(false)
local context = {
title = "测试",
name = "lucy",
description = "<script>alert(1);</script>",
age = 40,
hobby = {"电影", "音乐", "阅读"},
score = {语文 = 90, 数学 = 80, 英语 = 70},
score2 = {
{name = "语文", score = 90},
{name = "数学", score = 80},
{name = "英语", score = 70},
}
}
template.render("view.html", context)
模板·
{(header.html)}
<body>
{# 不转义变量输出 #}
姓名:{* string.upper(name) *}<br/>
{# 转义变量输出 #}
简介:{{description}}
简介:{* description *}<br/>
{# 可以做一些运算 #}
年龄: {* age + 10 *}<br/>
{# 循环输出 #}
爱好:
{% for i, v in ipairs(hobby) do %}
{% if v == '电影' then %} - xxoo
{%else%} - {* v *}
{% end %}
{% end %}<br/>
成绩:
{% local i = 1; %}
{% for k, v in pairs(score) do %}
{% if i > 1 then %},{% end %}
{* k *} = {* v *}
{% i = i + 1 %}
{% end %}<br/>
成绩2:
{% for i = 1, #score2 do local t = score2[i] %}
{% if i > 1 then %},{% end %}
{* t.name *} = {* t.score *}
{% end %}<br/>
{# 中间内容不解析 #}
{-raw-}{(file)}{-raw-}
{(footer.html)}
8.9 layout 布局统一风格
也就是支持引入其他文件,比如统一的头和尾。
使用模板内容嵌套可以实现全站风格同一布局
lua
local template = require "resty.template"
一、
local layout = template.new "layout.html"
layout.title = "Testing lua-resty-template"
layout.view = template.compile "view.html" { message = "Hello, World!" }
layout:render()
8.10 Redis缓存+mysql+模板输出
cjson = require "cjson"
sql="select * from t_emp"
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:get(sql)
if not res then
ngx.say("failed to get sql: ", err)
return
end
if res == ngx.null then
ngx.say("sql"..sql.." not found.")
--mysql查询
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("failed to instantiate mysql: ", err)
return
end
db:set_timeout(1000) -- 1 sec
local ok, err, errcode, sqlstate = db:connect{
host = "192.168.44.211",
port = 3306,
database = "zhangmen",
user = "root",
password = "111111",
charset = "utf8",
max_packet_size = 1024 * 1024,
}
ngx.say("connected to mysql.<br>")
res, err, errcode, sqlstate =
db:query(sql)
if not res then
ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
--ngx.say("result: ", cjson.encode(res))
ok, err = red:set(sql, cjson.encode(res))
if not ok then
ngx.say("failed to set sql: ", err)
return
end
ngx.say("set result: ", ok)
return
end
local template = require("resty.template")
template.caching(false)
local context = {
title = "测试",
name = "lucy",
description = "<script>alert(1);</script>",
age = 40,
hobby = {"电影", "音乐", "阅读"},
score = {语文 = 90, 数学 = 80, 英语 = 70},
score2 = {
{name = "语文", score = 90},
{name = "数学", score = 80},
{name = "英语", score = 70},
},
zhangmen=cjson.decode(res)
}
template.render("view.html", context)
{(header.html)}
<body>
{# 不转义变量输出 #}
姓名:{* string.upper(name) *}<br/>
{# 转义变量输出 #}
年龄: {* age + 10 *}<br/>
{# 循环输出 #}
爱好:
{% for i, v in ipairs(hobby) do %}
{% if v == '电影' then %} - xxoo
{%else%} - {* v *}
{% end %}
{% end %}<br/>
成绩:
{% local i = 1; %}
{% for k, v in pairs(score) do %}
{% if i > 1 then %},{% end %}
{* k *} = {* v *}
{% i = i + 1 %}
{% end %}<br/>
成绩2:
{% for i = 1, #score2 do local t = score2[i] %}
{% if i > 1 then %},{% end %}
{* t.name *} = {* t.score *}
{% end %}<br/>
{# 中间内容不解析 #}
{-raw-}{(file)}{-raw-}
掌门:
{* zhangmen *}
{% for i = 1, #zhangmen do local z = zhangmen[i] %}
{* z.deptId *},{* z.age *},{* z.name *},{* z.empno *},<br>
{% end %}<br/>
{(footer.html)}
这个文件内容做三件事
第一首先redis查询是否有该数据,如果有则返回,如果没有则去数据库中查,查询到在放入缓存,最后将结果渲染