Nginx Lua API
说明
Lua最大的作用就是接收Nginx收到的海量请求,处理后再输出响应结果。
对于请求,无非就是接收参数(
parameter
)、解析请求头(header
)和请求体(body
)等信息。对于响应,无非就是输出响应的状态码、响应头和响应内容体。
所谓API
就是调用Nginx中提供的内置变量和基本方法来完成响应请求、处理内容和输出数据。
而模块
则是基于Nginx插拔式的模块架构,实现对功能的扩展。
xxx_by_lua_block
:和前一种一样,只不过将代码组织为代码块,可读性更强一些,前面的小栗子也有这种示范。xxx_by_lua_file
:将Lua脚本放到独立的外部文件中,Nginx只需引用文件即可,前面的代码组织使用的就是这种方式。
上面的xxx
表示不同的Nginx模块指令。
请求处理
ngx.var
:获取Nginx中定义的变量和它的内置变量。ngx.req
:Nginx请求模块相关API
。ngx.header
:Nginx响应头相关API
。ngx.time
:Nginx时间相关API
。ngx.re
:Nginx正则表达式相关API
。
还有一个高频使用的ngx.say
。
获取Nginx中定义的变量和它的内置变量(完整的Nginx内置变量可以参考这里)。
先修改之前lua.conf
中的内容。
> vi /usr/local/openresty/nginx/conf/lua.conf
server {
listen 9527;
server_name _;
location /request1/(\d+)/(\d+) {
# 声明两个nginx变量
set $a $1;
set $b $host;
default_type 'text/html';
# 关闭lua代码编译缓存,让lua文件实时生效(生产环境最好是on)
lua_code_cache off;
# 调用test_api.lua脚本
content_by_lua_file conf/lua/test_api.lua;
# 内容体处理完成后调用
echo_after_body "ngx.var.b $b";
}
location /request2 {
default_type 'text/html';
lua_code_cache off;
content_by_lua_block {
ngx.header.content_type = 'text/html'
ngx.header.first_header = "hello nginx"
ngx.header.second_header = "hello openresty"
ngx.header["third_header"] = "hello lua"
ngx.header["forth_header"] = {"key1=a;value=1", "key2=b;value=2"}
local header = ngx.resp.get_headers()
for k, v in pairs(header) do
ngx.say(string.format("Header name: %s, value: %s", k, v), "<br>")
end
}
}
}
然后在/usr/local/openresty/nginx/conf/lua
文件夹中创建一个名为test_api.lua
的文件,并输入下面的内容。
> cd /usr/local/openresty/nginx/conf/lua
> vi test_api.lua
-- 声明nginx变量
ngx.say("====== var begin ======", "<br>")
local var = ngx.var
ngx.var.b = 2;
ngx.say("ngx.var.a ==> ", var.a, "<br>")
ngx.say("ngx.var.b ==> ", var.b, "<br>")
ngx.say("ngx.var[2] ==> ", var[2], "<br>")
ngx.say("====== var begin ======", "<br>")
ngx.say("<br>")
-- 处理请求头
ngx.say("====== headers begin ======", "<br>")
local headers = ngx.req.get_headers()
-- $arg_name表示名为name的参数,如果有age参数,那么同样可以用ngx.var.arg_age获取它的值
ngx.say("$arg_name ==> ", ngx.var.arg_name, "<br>")
ngx.say("$args ==> ", ngx.var.args, "<br>")
ngx.say("$binary_remote_addr ==> ", ngx.var.binary_remote_addr, "<br>")
ngx.say("$body_bytes_sent ==> ", ngx.var.body_bytes_sent, "<br>")
ngx.say("$content_length ==> ", ngx.var.content_length, "<br>")
ngx.say("$content_type ==> ", ngx.var.content_type, "<br>")
ngx.say("$content_type ==> ", ngx.var.content_type, "<br>")
ngx.say("$uri ==> ", ngx.var.uri, "<br>")
ngx.say("$document_uri ==> ", ngx.var.document_uri, "<br>")
ngx.say("$host ==> ", ngx.var.host, "<br>")
ngx.say("$hostnamet ==> ", ngx.var.hostname, "<br>")
ngx.say("$http_cookie ==> ", ngx.var.http_cookie, "<br>")
ngx.say("$http_referer ==> ", ngx.var.http_referer, "<br>")
ngx.say("$http_user_agent ==> ", ngx.var.http_user_agent, "<br>")
ngx.say("$http_via ==> ", ngx.var.http_via, "<br>")
ngx.say("$http_x_forwarded_for ==> ", ngx.var.http_x_forwarded_for, "<br>")
ngx.say("$is_args ==> ", ngx.var.is_args, "<br>")
ngx.say("$limit_rate ==> ", ngx.var.limit_rate, "<br>")
ngx.say("$nginx_version ==> ", ngx.var.nginx_version, "<br>")
ngx.say("$pid ==> ", ngx.var.pid, "<br>")
ngx.say("$query_string ==> ", ngx.var.query_string, "<br>")
ngx.say("$realpath_root ==> ", ngx.var.realpath_root, "<br>")
ngx.say("$remote_addr ==> ", ngx.var.remote_addr, "<br>")
ngx.say("$remote_port ==> ", ngx.var.remote_port, "<br>")
ngx.say("$remote_user ==> ", ngx.var.remote_user, "<br>")
ngx.say("$request ==> ", ngx.var.request, "<br>")
ngx.say("$request_body ==> ", ngx.var.request_body, "<br>")
ngx.say("$request_body_file ==> ", ngx.var.request_body_file, "<br>")
ngx.say("$request_completion ==> ", ngx.var.request_completion, "<br>")
ngx.say("$request_filename ==> ", ngx.var.request_filename, "<br>")
ngx.say("$request_method ==> ", ngx.var.request_method, "<br>")
ngx.say("$request_uri ==> ", ngx.var.request_uri, "<br>")
ngx.say("$scheme ==> ", ngx.var.scheme, "<br>")
ngx.say("$server_addr ==> ", ngx.var.server_addr, "<br>")
ngx.say("$server_name ==> ", ngx.var.server_name, "<br>")
ngx.say("$server_port ==> ", ngx.var.server_port, "<br>")
ngx.say("$server_protocol ==> ", ngx.var.server_protocol, "<br>")
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
if type(v) == "table" then
ngx.say(k, ":", table.concat(v, ","), "<br>")
else
ngx.say(k, " : ", v, "<br>")
end
end
ngx.say("====== headers end ======", "<br>")
ngx.say("<br>")
-- 获取GET方法的全部请求参数
ngx.say("====== GET uri args begin ======", "<br>")
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
ngx.say("====== GET uri args end ======", "<br>")
ngx.say("<br>")
-- 获取POST方法的全部请求参数
ngx.say("====== POST args begin ======", "<br>")
ngx.req.read_body()
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
ngx.say("====== POST args end ======", "<br>")
ngx.say("<br>")
-- 请求的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>")
ngx.say("<br>")
-- 获取时间
ngx.say("====== time begin ======", "<br>")
local t1 = ngx.now()
for i = 1, 1000000 do
end
ngx.update_time()
local t2 = ngx.now()
ngx.say(t1, " , ", t2, "<br>")
local time_now = ngx.time()
local formatted_time = os.date("%Y-%m-%d %H:%M:%S %p", time_now)
ngx.say("current time ==> ", formatted_time, "<br>")
ngx.say("====== time end ======", "<br>")
ngx.say("<br>")
-- 编码解码
ngx.say("====== encode decode begin ======", "<br>")
local request_uri = ngx.var.request_uri;
-- MD5
ngx.say("ngx.md5('123456') ==> ", ngx.md5("123456"), "<br/>")
-- 编码
local escape_code = ngx.escape_uri(request_uri);
ngx.say("encode request_uri ==> ", ngx.escape_uri(request_uri), "<br/>");
-- 解码
ngx.say("decode request_uri ==> ", ngx.unescape_uri(escape_code), "<br/>");
-- 参数编码
local pos, _ = string.find(request_uri, '?')
if pos > 0 then
local uri = string.sub(request_uri, 1, pos - 1)
ngx.say("uri sub = ", string.sub(request_uri, pos + 1), "<br/>");
-- 对字符串解码
local args = ngx.decode_args(string.sub(request_uri, pos + 1))
for k,v in pairs(args) do
ngx.say("k = ", k, ", v = ", v, "<br/>");
end
if args and args.userId then
args.userId = args.userId + 10000
ngx.say("args + 10000 : ", uri .. '?' .. ngx.encode_args(args), "<br/>");
end
end
ngx.say("====== encode decode end ======", "<br>")
ngx.say("<br>")
-- 正则表达式
ngx.say("====== regexp begin ======", "<br>")
local m, err = ngx.re.match("hello, 123456", "[0-9]+")
if m then
-- 打印出123456
ngx.say("regexp ==> ", m[0])
else
if err then
-- 打印日志
ngx.log(ngx.ERR, "error ==> ", err)
return
end
ngx.say("未找到匹配项")
end
ngx.say("<br>")
ngx.say("====== regexp end ======", "<br>")
ngx.say("<br>")
把这个地址http://服务器IP:9527/request1/1/2?a=3&b=4&name=lixingyun放在浏览器中访问,和用Postman工具以POST
方式调用它,输出的内容会有很多不同。
然后访问http://服务器IP:9527/request2就能得到HTTP
头输出的内容。
重定向与数据共享
接下来是ngx.redirect
和ngx.ctx
。
修改/usr/local/openresty/nginx/conf/nginx.conf
文件。
> vi /usr/local/openresty/nginx/conf/nginx.conf
# 在http部分分配共享内存大小
lua_shared_dict global_shared 1m;
修改lua.conf
文件,输入下面的内容。
> vi /usr/local/openresty/nginx/conf/lua.conf
server {
listen 9527;
server_name _;
location = /test1 {
default_type 'text/html';
lua_code_cache off;
content_by_lua_block {
ngx.say([[this is test1]])
}
}
location = /test2 {
default_type 'text/html';
lua_code_cache off;
rewrite_by_lua_block {
return ngx.redirect('/test1');
}
}
# 通过ngx.ctx实现共享
location = /test3 {
default_type 'text/html';
lua_code_cache off;
rewrite_by_lua_block {
ngx.ctx.data = 1
}
access_by_lua_block {
ngx.ctx.data = ngx.ctx.data + 1
}
content_by_lua_block {
ngx.say(ngx.ctx.data)
}
}
# 通过ngx.shared实现共享
location = /test4 {
default_type 'text/html';
lua_code_cache off;
content_by_lua_block {
local global_shared = ngx.shared.global_shared
local data = global_shared:get("data")
if not data then
data = 1
global_shared:set("data", data)
-- 输出 data ==> 1
ngx.say("data ==> ", data, "<br/>")
end
data = global_shared:incr("data", data)
-- 输出 data ==> 2
ngx.say("data ==> ", data, "<br/>")
}
}
}
在浏览器中访问http://服务器IP:9527/test2,会发现地址栏和页面已经被重定向到了http://服务器IP:9527/test1。
而访问http://服务器IP:9527/test3之后,页面上打印出了2
。
日志
最后是ngx.log
。
ngx.log
:Nginx日志API
。
Nginx中定义的日志级别如下。
级别 | 说明 |
---|---|
ngx.EMERG | 紧急事故 |
ngx.CRIT | 严重 |
ngx.ERR | 错误 |
ngx.WARN | 警告 |
ngx.ALERT | 报警 |
ngx.NOTICE | 提醒 |
ngx.STDERR | 标准错误 |
ngx.INFO | 信息 |
ngx.DEBUG | 调试 |
这次需要修改/usr/local/openresty/nginx/conf/nginx.conf
文件。
> vi /usr/local/openresty/nginx/conf/nginx.conf
# 测试四个日志级别,指定日志输出目的地
error_log logs/error.log error;
error_log logs/error.log warn;
error_log logs/error.log notice;
error_log logs/error.log info;
......
server {
listen 9527;
server_name localhost;
location /log {
default_type 'text/html';
lua_code_cache off;
content_by_lua_block {
local a = 1
local b = "string"
local c
local d = 3.14
# 所有的日志内容都会输出到logs/error.log中
ngx.log(ngx.ERR, "a ==> ", a)
ngx.log(ngx.WARN, "b ==> ", b)
# print等价于ngx.log(ngx.NOTICE, ...)
print([[this is print]])
ngx.log(ngx.NOTICE, "c ==> ", c)
ngx.log(ngx.INFO, "d ==> ", d)
}
}
location / {
root /usr/local/openresty/nginx/html;
index index.html index.htm;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin *;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
......
在浏览器中访问http://服务器IP:9527/log,然后到Nginx的日志文件中查看输出的日志记录。
> cat /usr/local/openresty/nginx/logs/error.log
2023/06/04 19:47:26 [error] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):6: a ==> 1, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [error] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):6: a ==> 1, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [error] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):6: a ==> 1, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [error] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):6: a ==> 1, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [warn] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):7: b ==> string, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [warn] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):7: b ==> string, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [warn] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):7: b ==> string, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [notice] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):8: this is print, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [notice] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):8: this is print, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [notice] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):9: c ==> nil, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [notice] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):9: c ==> nil, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
2023/06/04 19:47:26 [info] 55838#55838: *54 [lua] content_by_lua(nginx.conf:49):10: d ==> 3.14, client: 127.0.0.1, server: localhost, request: "GET /log HTTP/1.1", host: "localhost"
更多有关ngx
的信息可以看这几个地方。
核心常量:https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#core-constants。
日志级别:https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#nginx-log-level-constants。
HTTP方法:https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#http-method-constants。
HTTP状态码:https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#http-status-constants。
感谢支持
更多内容,请移步《超级个体》。