前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenResty实战系列 | Nginx Lua API 接口开发

OpenResty实战系列 | Nginx Lua API 接口开发

作者头像
Tinywan
发布2024-07-16 15:00:57
2480
发布2024-07-16 15:00:57
举报
文章被收录于专栏:开源技术小栈

概述

OpenResty 为开发者提供了一系列强大的API,这些API使得Lua脚本能够与Nginx紧密交互,从而高效地执行多种Web服务器任务。在处理Web服务器的核心工作流程中,主要包括三个环节:接收请求、处理请求以及输出响应。在接收请求时,我们能够获取到请求参数、请求头部以及请求体等关键信息。处理请求则涉及执行特定的Lua代码逻辑。至于输出响应,则需要设定响应状态码、自定义响应头部以及构造响应内容体。

在Web开发的典型流程中,接收请求、处理请求并输出响应是三个核心环节。OpenResty以其独特的方式优化了这些环节的处理过程:

  1. 接收请求:OpenResty允许Lua脚本直接访问到请求的各个组成部分,包括但不限于请求参数(无论是URL中的查询参数还是POST请求体中的字段)、请求头信息以及完整的请求体内容。这种直接访问能力让开发者能够轻松解析并理解客户端的请求意图,为后续的处理逻辑提供坚实的数据基础。
  2. 处理请求:一旦请求被接收并解析,OpenResty便通过其提供的Lua API调用相应的Lua代码来处理这些请求。得益于Lua语言的轻量级和高效性,以及OpenResty对Nginx内部机制的深度集成,这一处理过程既快速又灵活。开发者可以编写复杂的业务逻辑,调用外部服务,执行数据库操作等,以满足各种业务需求。
  3. 输出响应:在处理完请求后,OpenResty同样支持通过Lua脚本灵活地构建并输出响应。这包括设置响应状态码(如200 OK、404 Not Found等),添加或修改响应头信息(如Content-Type、Set-Cookie等),以及发送响应体内容。通过精细控制响应的各个方面,开发者能够确保客户端接收到准确、清晰且符合预期的响应。

接收请求

openresty.tinywan.com.conf配置文件

代码语言:javascript
复制
server {
    listen 80;
    server_name openresty.tinywan.com;

    location ~ /lua_request/(\d+)/(\d+) {
        default_type "text/html";
        lua_code_cache off;
        # 设置nginx变量
        set $a $1;
        set $b $host;
        # nginx内容处理
        content_by_lua_file conf/lua/request_test.lua;
        # 内容体处理完成后调用
        echo_after_body "[x] 内容体处理完成后调用 ngx.var.b : $b";
    }
}

request_test.lua文件代码

代码语言:javascript
复制
--[[--------------------------------------------------------      
* |  Copyright (C) Shaobo Wan (Tinywan)
* |  Origin: 开源技术小栈
* |-------------------------------------------------------- 
--]]

--接受Nginx变量  ngx.var 访问Nginx变量,例如客户端IP地址、请求URI等。
local var = ngx.var  
ngx.say("[x] ngx.var.a : ", var.a)  
ngx.say("[x] ngx.var.b : ", var.b)  
ngx.say("[x] ngx.var[2] : ", var[2])  
ngx.var.b = "Tinywan Openresty";  
  
ngx.say("\r\n")  
  
--请求头  
local headers = ngx.req.get_headers()  
ngx.say("[x] headers begin")  
ngx.say("[x] Host : ", headers["Host"])  
ngx.say("[x] user-agent1 : ", headers["user-agent"])  
ngx.say("[x] user-agent2 : ", headers.user_agent)  
for k,v in pairs(headers) do  
    if type(v) == "table" then  
        ngx.say(k, " : ", table.concat(v, ","))  
    else  
        ngx.say(k, " : ", v)  
    end  
end  
ngx.say("[x] headers end")  
ngx.say("\r\n") 
  
--get请求uri参数  
ngx.say("[x] uri args begin")  
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, ", "))  
    else  
        ngx.say(k, ": ", v)  
    end  
end  
ngx.say("[x] uri args end")  
ngx.say("\r\n") 

--post请求参数  
ngx.req.read_body()  
ngx.say("[x] post args begin")  
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, ", "))  
    else  
        ngx.say(k, ": ", v)  
    end  
end  
ngx.say("[x] post args end")  
ngx.say("\r\n") 

--请求的http协议版本  
ngx.say("[x] ngx.req.http_version : ", ngx.req.http_version())  
--请求方法  
ngx.say("[x] ngx.req.get_method : ", ngx.req.get_method())  
--原始的请求头内容  
ngx.say("[x] ngx.req.raw_header : ",  ngx.req.raw_header())  
--请求的body内容体  
ngx.say("[x] ngx.req.get_body_data() : ", ngx.req.get_body_data())  

通过curl脚本测试请求打印结果

代码语言:javascript
复制
$ curl -i -H "Content-Type:application/json" -X POST -d '{"name":"ShaoBoWan","age":24}' http://openresty.tinywan.com/lua_request/2024/12/?name=Tinywan&schoole=Ggoogle
[1] 1264
HTTP/1.1 200 OK
Server: openresty/1.17.8.2
Date: Tue, 16 Jul 2024 00:52:36 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding

[x] ngx.var.a : 2024
[x] ngx.var.b : openresty.tinywan.com
[x] ngx.var[2] : 12


[x] headers begin
[x] Host : openresty.tinywan.com
[x] user-agent1 : curl/7.70.0
[x] user-agent2 : curl/7.70.0
host : openresty.tinywan.com
content-type : application/json
user-agent : curl/7.70.0
accept : */*
content-length : 29
[x] headers end


[x] uri args begin
name: Tinywan
[x] uri args end


[x] post args begin
{"name":"ShaoBoWan","age":24}: true
[x] post args end


[x] ngx.req.http_version : 1.1
[x] ngx.req.get_method : POST
[x] ngx.req.raw_header : POST /lua_request/2024/12/?name=Tinywan HTTP/1.1
Host: openresty.tinywan.com
User-Agent: curl/7.70.0
Accept: */*
Content-Type:application/json
Content-Length: 29


[x] ngx.req.get_body_data() : {"name":"ShaoBoWan","age":24}
[x] 内容体处理完成后调用 ngx.var.b :Tinywan Openresty
[1]+  Done                    curl -i -H "Content-Type:application/json" -X POST -d '{"name":"ShaoBoWan","age":24}' http://openresty.tinywan.com/lua\_request/2024/12/?name=Tinywan
  • ngx.var :nginx变量,如果要赋值如ngx.var.b = 2,此变量必须提前声明;另外对于``nginx location中使用正则捕获的捕获组可以使用ngx.var[捕获组数字]获取;
  • ngx.req.get_headers:获取请求头,默认只获取前100,如果想要获取所以可以调用ngx.req.get_headers(0);获取带中划线的请求头时请使用如headers.user_agent这种方式;如果一个请求头有多个值,则返回的是lua table
  • ngx.req.get_uri_args:获取url请求参数,其用法和get_headers类似;
  • ngx.req.get_post_args:获取post请求内容体,其用法和get_headers类似,但是必须提前调用ngx.req.read_body()来读取body体(也可以选择在nginx配置文件使用lua_need_request_body on;开启读取body体,但是官方不推荐);
  • ngx.req.raw_header:未解析的请求头字符串;
  • ngx.req.get_body_data:为解析的请求body体内容字符串。

处理请求

openresty.tinywan.com.conf配置文件

代码语言:javascript
复制
location /lua_response_02 {  
    default_type "text/html";
    lua_code_cache off; 
    content_by_lua_file conf/lua/response_test_02.lua;
}

response_test_02.lua脚本代码

代码语言:javascript
复制
ngx.redirect("https://www.tinywan.com", 302)

通过curl脚本测试请求打印结果

代码语言:javascript
复制
$ curl -i http://openresty.tinywan.com/lua_response_02
HTTP/1.1 302 Moved Temporarily
Server: openresty/1.17.8.2
Date: Tue, 16 Jul 2024 01:13:26 GMT
Content-Type: text/html
Content-Length: 151
Connection: keep-alive
Location: https://www.tinywan.com

<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>openresty/1.17.8.2</center>
</body>
</html>
  • ngx.status=状态码,设置响应的状态码;
  • ngx.resp.get_headers()获取设置的响应状态码;
  • ngx.send_headers()发送响应状态码,当调用ngx.say/ngx.print时自动发送响应状态码;可以通过ngx.headers_sent=true判断是否发送了响应状态码。

openresty.tinywan.com.conf配置文件

代码语言:javascript
复制
location /lua_response_03 {  
    default_type "text/html";
    lua_code_cache off; 
    content_by_lua_file conf/lua/response_test_03.lua;
}

response_test_03.lua脚本代码

代码语言:javascript
复制
--[[---------------------------------------------------------    
* |  Copyright (C) Shaobo Wan (Tinywan)
* |  Origin: 开源技术小栈
* |-----------------------------------------------------------
--]]

--未经解码的请求uri  
local request_uri = ngx.var.request_uri;  
ngx.say("[x] request_uri : ", request_uri);  
--解码  
ngx.say("[x] decode request_uri : ", ngx.unescape_uri(request_uri));  
--MD5  
ngx.say("[x] ngx.md5 : ", ngx.md5("123"))  
--http time  
ngx.say("[x] ngx.http_time : ", ngx.http_time(ngx.time()))  

通过curl脚本测试请求打印结果

代码语言:javascript
复制
$ curl -i http://openresty.tinywan.com/lua_response_03
HTTP/1.1 200 OK
Server: openresty/1.17.8.2
Date: Tue, 16 Jul 2024 01:38:43 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding

[x] request_uri : /lua_response_03
[x] decode request_uri : /lua_response_03
[x] ngx.md5 : 202cb962ac59075b964b07152d234b70
[x] ngx.http_time : Tue, 16 Jul 2024 01:38:43 GMT

如果访问出现500 Internal Server Error 请通过nginx错误日志排查,下面错误表示缺少一个结束符;

代码语言:javascript
复制
[error] 7#7: *2 failed to load external Lua file 
"/usr/local/openresty/nginx/conf/lua/response_test_03.lua": 
/usr/local/openresty/nginx/conf/lua/response_test_03.lua:13: unfinished string near '")  ', 
client: 172.18.0.1, 
server: openresty.tinywan.com, 
request: "GET /lua_response_03 HTTP/1.1", 
host: "openresty.tinywan.com"

输出响应

openresty.tinywan.com.conf配置文件

代码语言:javascript
复制
server {
    listen 80;
    server_name openresty.tinywan.com;

    location /lua_response_01 {  
        default_type "text/html";
        lua_code_cache off; 
        content_by_lua_file conf/lua/response_test_01.lua;
    }
}

response_test_01.lua脚本代码

代码语言:javascript
复制
--[[---------------------------------------------------------    
* |  Copyright (C) Shaobo Wan (Tinywan)
* |  Origin: 开源技术小栈
* |-----------------------------------------------------------
--]]
--写响应头  
ngx.header.age = "24"  
--多个响应头可以使用table  
ngx.header.name = {"Tinywan", "ShaoBoWan"}  
--输出响应  
ngx.say("[x] age", "name")  
ngx.print("[x] age", "name")  
--200状态码退出  
return ngx.exit(200)  

通过curl脚本测试请求打印结果

代码语言:javascript
复制
$ curl -i http://openresty.tinywan.com/lua_response_01


HTTP/1.1 200 OK
Server: openresty/1.17.8.2
Date: Tue, 16 Jul 2024 01:09:51 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
age: 24
name: Tinywan
name: ShaoBoWan

[x] agename
[x] agename
  • ngx.header:输出响应头;
  • ngx.print:输出响应内容体;
  • ngx.say:通ngx.print,但是会最后输出一个换行符;
  • ngx.exit:指定状态码退出。

Nginx全局内存

Nginx是一个Master进程多个Worker进程的工作方式,因此我们可能需要在多个Worker进程中共享数据。对于全局内存的配置,Nginx提供了lua_shared_dict指令,允许在Nginx的http部分分配内存大小,定义一块共享内存空间,所有worker进程都可见 6。这种共享内存机制类似于Java中的Ehcache进程内本地缓存,允许在多个Worker进程间共享数据 6。例如,可以使用以下语法分配10MB的共享内存:

代码语言:javascript
复制
http {
    # 共享全局变量,在所有worker间共享  
    lua_shared_dict shared_resty_data 1m;
    ...

    server {
        listen 80;
        server_name openresty.tinywan.com;
    
        location /lua_shared_dict {  
            default_type "text/html";
            lua_code_cache off; 
            content_by_lua_file conf/lua/lua_shared_dict_test.lua;
        }
    }
}

在使用共享内存时,可以通过Lua代码进行操作,例如获取、设置、删除共享内存中的键值对 6。例如,使用以下Lua代码可以获取和设置共享内存中的值。lua_shared_dict_test.lua 脚本文件

代码语言:javascript
复制
--1、获取全局共享内存变量  
local resty_shared_data = ngx.shared.shared_resty_data  
  
--2、获取字典值  
local i = resty_shared_data:get("i")  
if not i then  
    i = 1  
    --3、惰性赋值  
    resty_shared_data:set("i", i)  
    ngx.say("[x] lazy set i ", i)  
end  
--4、递增  
i = resty_shared_data:incr("i", 1)  
ngx.say("[x] i = ", i)  

此外,还有get_stalesafe_setaddsafe_addreplace等方法,用于处理共享内存中的数据,包括处理过期键和避免内存不足时的强制删除操作。

Nginx全局变量是存储在服务器进程内存中的数据,用于在配置和运行时提供各种信息,可以分为常量变量、内置变量和自定义变量 5。全局变量的使用可以提高配置的灵活性,简化管理任务,并提供对服务器运行状况的深入了解。

请参考http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 接收请求
  • 处理请求
  • 输出响应
  • Nginx全局内存
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档