企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 概述 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`配置文件 ``` 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`文件代码 ```lua --[[-------------------------------------------------------- * | 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脚本测试请求打印结果 ``` $ 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 ``` ![](https://img.kancloud.cn/08/d4/08d4708ec2d15c93d2b1f111abac3ef9_1624x956.png) * **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`配置文件 ``` 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`脚本代码 ``` ngx.redirect("https://www.tinywan.com", 302) ``` 通过curl脚本测试请求打印结果 ``` $ 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`配置文件 ``` 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`脚本代码 ``` --[[--------------------------------------------------------- * | 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脚本测试请求打印结果 ``` $ 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错误日志排查,下面错误表示缺少一个结束符`;` ``` [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`配置文件 ``` 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`脚本代码 ```lua --[[--------------------------------------------------------- * | 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脚本测试请求打印结果 ``` $ 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的共享内存: ``` 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` 脚本文件 ``` --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_stale`、`safe_set`、`add`、`safe_add`、`replace`等方法,用于处理共享内存中的数据,包括处理过期键和避免内存不足时的强制删除操作。 Nginx全局变量是存储在服务器进程内存中的数据,用于在配置和运行时提供各种信息,可以分为常量变量、内置变量和自定义变量 5。全局变量的使用可以提高配置的灵活性,简化管理任务,并提供对服务器运行状况的深入了解。 请参考[http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT](http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT)。