Nginx结合Lua做动态加载(四)
#紧接上文,我们consul的使用已经总结的差不多了,但是是不是感觉没有展现一个实际的场景来把consul结合起来使其作用更大化呢?
我下面描述一个具体的场景:一般我们会有一套稳定环境,但是会有N多套测试需求环境,而且这是一个动态变化的过程,这里我们需求用需求号代替,比如今天测试一个集群的功能创建了一个2001的需求,后天又创建了一个2002的需求,那么正常来说你访问网站是不是就变成了aaa-2001.xxx.com/aaa-2002.xxx.com,这就带来一个问题测试起来太繁琐了(每次测试你都要更换URL),现在很多都是app测试了,app更换域名还是挺麻烦的。还有一个问题现在很多都是微服务架构,就是你是不是要1比1的部署测试环境,不然你的测试流程很可能走不下去。
那么我们怎么解决上述问题呢,一方面让测试更便捷另一方面让成本更低,我们可以试想一下:先提供一个web平台用户可以选择自己要访问哪个环境,然后nginx+lua成为网关,一个固定域名基于来源来判断要访问哪个环境并路由,然后后端是nginx+consul将请求转发到执行的需求环境容器,然后需求号再作为tag一直透传下去,有对应的需求就将请求交给对应的需求容器,没有对应的需求容器就将请求交给稳定环境是不是就解决了我们上面提到的问题,好了大体思路有了我们去实现它。
一、Nginx+Lua简单使用
1.1 简单方式直接使用openresty
1.1.1 openresty安装
#官网:https://openresty.org/en/
#nginx使用到的模块如何使用:https://github.com/openresty/lua-nginx-module#description
# yum install -y readline-devel pcre-devel openssl-devel
# wget https://openresty.org/download/openresty-1.27.1.1.tar.gz
# tar xf openresty-1.27.1.1.tar.gz
# cd openresty-1.27.1.1/
# ./configure --prefix=/opt/soft/openresty --with-luajit --with-http_stub_status_module --with-pcre --with-pcre-jit
# gmake && gmake install
# vim /opt/soft/openresty/nginx/conf/nginx.conf #在server区域中加上这么一句调用lua
location /hello { default_type text/html; content_by_lua 'ngx.say("hello,lua scripts")'; }
# /opt/soft/openresty/nginx/sbin/nginx
# curl 127.0.0.1/hello #直接curl调用测试一下会看到有内容数据,说明lua调用成功
hello,lua scripts
1.2 nginx编译安装支持lua
安装luajit
# wget https://codeload.github.com/openresty/luajit2/tar.gz/refs/tags/v2.1-20250117
# tar xf luajit2-2.1-20250117.tar.gz
# cd luajit2-2.1-20250117
# make install PREFIX=/usr/local/luajit
# vim /etc/profile.d/lua.sh
export LUAJIT_LIB=/usr/local/luajit/lib export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1
# source /etc/profile.d/lua.sh
# echo "/usr/local/luajit/lib/" >>/etc/ld.so.conf.d/lua.conf
# ldconfig
#首先安装2.1版本的lua,因为我们是用OpenResty得lua-resty-core会提示需要luajit2.1,下面为报错提醒[注意但是不影响nginx启动,所以大家常用的https://codeload.github.com/LuaJIT/LuaJIT/tar.gz/refs/tags/v2.0.4 还是可以用的 ]:
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html) nginx: [alert] [lua] base.lua:42: use of lua-resty-core with LuaJIT 2.0 is not recommended; use LuaJIT 2.1+ instead nginx: [alert] [lua] lrucache.lua:25: use of lua-resty-lrucache with LuaJIT 2.0 is not recommended; use LuaJIT 2.1+ instead
#然后你说我安装官网的 https://github.com/LuaJIT/LuaJIT/archive/refs/tags/v2.1.ROLLING.tar.gz 好了吧,但是还是会有提醒依旧不会影响你服务启动,下面提醒:
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
安装需要的lua模块
#wget https://codeload.github.com/openresty/lua-nginx-module/tar.gz/refs/tags/v0.10.28
#tar xf lua-nginx-module-0.10.28.tar.gz
#wget https://codeload.github.com/openresty/lua-resty-core/tar.gz/refs/tags/v0.1.31
#tar xf lua-resty-core-0.1.31.tar.gz
#cd lua-resty-core-0.1.31
#make install PREFIX=/usr/local/lua_core
#wget https://codeload.github.com/openresty/lua-resty-lrucache/tar.gz/refs/tags/v0.15
#tar xf lua-resty-lrucache-0.15.tar.gz
#cd lua-resty-lrucache
#make install PREFIX=/usr/local/lua_core
安装nginx并配置lua进行测试
# yum install pcre pcre-devel openssl openssl-devel gd gd-devel -y
#wget https://nginx.org/download/nginx-1.28.0.tar.gz
#tar xf nginx-1.28.0.tar.gz
# cd nginx-1.28.0/
#./configure --prefix=/opt/soft/nginx --sbin-path=/opt/soft/nginx/sbin/nginx --conf-path=/opt/soft/nginx/main-conf/nginx.conf --error-log-path=/opt/log/nginx/error.log --http-log-path=/opt/log/nginx/access.log --pid-path=/opt/soft/nginx/run/nginx.pid --lock-path=/opt/soft/nginx/run/nginx.lock --user=work --group=work --http-client-body-temp-path=/opt/soft/nginx/cache/client_temp --http-proxy-temp-path=/opt/soft/nginx/cache/proxy_temp --http-fastcgi-temp-path=/opt/soft/nginx/cache/fastcgi_temp --http-uwsgi-temp-path=/opt/soft/nginx/cache/uwsgi_tmp --http-scgi-temp-path=/opt/soft/nginx/cache/scgi_temp --with-http_v2_module --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-http_sub_module --with-http_gzip_static_module --with-pcre --with-http_addition_module --with-http_image_filter_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_gzip_static_module --with-file-aio --with-http_random_index_module --with-ld-opt=-Wl,-rpath,/usr/local/luajit/lib --add-module=/opt/soft/package/nginx_lua/lua-nginx-module-0.10.28
#make -j 8
#make install
# vim /opt/soft/nginx/main-conf/nginx.conf #增加一条lua测试location
location /lua { default_type 'text/plain'; content_by_lua 'ngx.say("hello, lua")'; } location / { root html; index index.html index.htm; }
#mkdir /opt/soft/nginx/cache
#/opt/soft/nginx/sbin/nginx -t #测试是没有报错的所以语法没问题
#/opt/soft/nginx/sbin/nginx #报错如下:
nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found: no field package.preload['resty.core'] no file './resty/core.lua' no file '/usr/local/luajit/share/luajit-2.1/resty/core.lua' no file '/usr/local/share/lua/5.1/resty/core.lua' no file '/usr/local/share/lua/5.1/resty/core/init.lua' no file '/usr/local/luajit/share/lua/5.1/resty/core.lua' no file '/usr/local/luajit/share/lua/5.1/resty/core/init.lua' no file './resty/core.so' no file '/usr/local/lib/lua/5.1/resty/core.so' no file '/usr/local/luajit/lib/lua/5.1/resty/core.so' no file '/usr/local/lib/lua/5.1/loadall.so' no file './resty.so' no file '/usr/local/lib/lua/5.1/resty.so' no file '/usr/local/luajit/lib/lua/5.1/resty.so' no file '/usr/local/lib/lua/5.1/loadall.so') in /opt/soft/nginx/main-conf/nginx.conf:119
# vim /opt/soft/nginx/main-conf/nginx.conf #开启引用前面编译的lua模块
#gzip on; #就是增加下面这一段 lua_package_path "/usr/local/lua_core/lib/lua/?.lua;;";
# /opt/soft/nginx/sbin/nginx #启动没有再报错
# curl 127.0.0.1/lua #测试输出正常
hello, lua
1.3 nginx调用lua的简单介绍
lua在nginx的指令执行顺序:
我们顺着上面的流程图把贯穿整个 Nginx生命周期的Lua 脚本执行链路,也就是Nginx Lua 模块提供了一系列钩子函数(hook),允许我们在 Nginx 的不同阶段执行 Lua 脚本介绍一下。以下是这些钩子的主要作用和调用顺序:
#一、初始化阶段 init_by_lua钩子函数 #全局初始化阶段,这是Nginx启动时最早的一个阶段,发生在主进程(master process)启动期间,且只执行一次。 init_worker_by_lua钩子函数 #Worker初始化阶段,在每个worker进程启动时执行,每个worker 进程都会独立运行一次,初始化的内容仅对当前 worker进程有效 #二、TLS(传输层安全协议)相关的钩子函数主要用于处理与TLS 握手、证书动态加载以及客户端认证等相关的逻辑 ssl_client_hello_by_lua #TLS客户端握手阶段,在收到客户端的TLS ClientHello消息时触发,发生在SSL/TLS握手的最早阶段 ssl_certificate_by_lua #TLS证书动态加载阶段,在TLS握手期间,当客户端请求连接时触发。发生在SSL/TLS握手之前 ssl_session_fetch_by_lua #TLS会话恢复阶段,在TLS握手期间,尝试恢复之前保存的TLS会话时触发。发生在SSL/TLS握手之前 ssl_session_store_by_lua #TLS会话存储阶段,在TLS握手完成后,将新的TLS会话保存到外部存储时触发 #三、HTTP level阶段,Nginx处理HTTP请求的各个阶段 server_rewrite_by_lua #这个阶段主要用于URL重写和复杂的逻辑处理,如访问控制或动态内容生成 set_by_lua #允许通过Lua脚本计算并设置变量值。它通常用于实现复杂的逻辑处理,并返回结果,在请求的rewrite和access阶段之前完成 rewrite_by_lua #用于在Nginx的rewrite阶段执行Lua脚本,主要用于URL重写和转发等功能 access_by_lua #用于在访问阶段执行用户自定义的Lua代码,在请求到达后端服务前进行权限检查,可以用来做权限管理/动态路由/限流控制/黑名单机制 content_by_lua #处理请求内容并生成响应 balancer_by_lua #在Nginx的负载均衡阶段动态选择上游服务器,它主要用于实现复杂的、自定义的负载均衡逻辑,例如基于请求参数、哈希算法或动态服务发现来选择后端服务器。 header_filter_by_lua #允许用户在HTTP响应头发送给客户端之前执行自定义Lua脚本。这一阶段的主要用途是修改或设置响应头 body_filter_by_lua #用于在响应体发送到客户端之前对其进行修改。它允许开发者以流式处理的方式操作响应内容 log_filter_by_lua #允许使用Lua脚本在日志记录阶段执行自定义逻辑,可用于统计分析或添加额外信息到日志中 #四、Worker进程退出阶段 exit_worker_by_lua #当Nginx Worker进程需要关闭时(例如服务器重启、重新加载配置或正常关闭时),这一阶段适合用于清理资源、记录日志或通知外部系统等操作
Nginx调⽤ Lua 模块指令,Nginx的可插拔模块加载执⾏:
set_by_lua set_by_lua_file #设置Nginx变量,可以实现负载的赋值逻辑 access_by_lua access_by_lua_file #请求访问阶段处理, ⽤于访问控制 content_by_lua content_by_lua_file #内容处理器, 接受请求处理并输出响应
Nginx 调⽤ Lua API:
ngx.var #nginx变量 ngx.req.get_headers #获取请求头 ngx.req.get_uri_args #获取url请求参数 ngx.redirect #重定向 ngx.print #输出响应内容体 ngx.say #输出响应内容体,最后输出⼀个换⾏符 ngx.header #输出响应头
1.4 lua在nginx中的一些阶段简单用法介绍
1.4.1 初始阶段例子介绍
init_by_lua例子介绍:
# vim nginx.conf
--init_by_lua块:在这里我们定义了一个全局变量_G.my_global_variable和一个全局函数_G.say_hello init_by_lua ' -- 定义一个全局变量 _G.my_global_variable = "Hello from init_by_lua" -- 定义一个全局函数 _G.say_hello = function(name) return "Hello, " .. name .. "! This message is from init_by_lua." end '; server { ...... location /init_by_lua { --- content_by_lua块:在处理HTTP请求时,我们通过ngx.say输出了这个全局变量和调用了这个全局函数 content_by_lua ' -- 使用预加载的全局变量 ngx.say(_G.my_global_variable) -- 调用预加载的全局函数 local greeting = _G.say_hello("World") ngx.say(greeting) '; }
# curl 127.0.0.1/init_by_lua #访问一下查看效果
Hello from init_by_lua Hello, World! This message is from init_by_lua.
init_worker_by_lua例子介绍:
# vim nginx.conf
-- init_worker_by_lua块:当每个worker进程启动时,这段Lua代码会被执行一次。记录了每个worker进程的PID到Nginx的日志中,并且将这个PID存储到了全局变量 _G.worker_pid 中。 init_worker_by_lua ' -- 引入lua库,我们使用了ngx.log函数来记录不同级别的日志消息(如ERROR、WARN、INFO)。 local ngx_log = ngx.log local ngx_ERR = ngx.ERR local ngx_WARN = ngx.WARN local ngx_INFO = ngx.INFO -- 每个worker进程启动时输出一条日志 ngx_log(ngx_INFO, "Worker process started with pid: ", ngx.worker.pid()) -- 可以在这里初始化worker级别的一些变量或连接池等 _G.worker_pid = ngx.worker.pid() '; server { location /init_worker_by_lua { -- content_by_lua块:在处理HTTP请求时,可以访问在init_worker_by_lua中初始化的全局变量 _G.worker_pid,并将其作为响应的一部分返回给客户端 content_by_lua ' -- 在处理请求时可以使用worker级别初始化的数据 ngx.say("This request is handled by worker with pid: ", _G.worker_pid) '; }
# curl 127.0.0.1/init_worker_by_lua
This request is handled by worker with pid: 2871584
1.4.2 HTTP Level阶段介绍
rewrite_by_lua例子介绍:
#rewrite_by_lua 是早期的语法形式,直接将 Lua 脚本作为字符串传入。rewrite_by_lua_block 是更现代的形式,支持多行脚本编写,可读性更高。
#如果你的 Nginx 版本较新,建议使用 rewrite_by_lua_block,因为它更易读、更易维护。
# vim /opt/soft/nginx/main-conf/nginx.conf
location / { rewrite_by_lua ' local uri = ngx.var.uri ngx.log(ngx.ERR, "Original URI: ", uri) -- 判断是否以 /api/v2/ 开头 if not string.match(uri, "^/api/v2/") then -- 如果不是,则重写为 /api/v2/ 前缀 local new_uri = "/api/v2" .. uri ngx.log(ngx.ERR, "Rewritten URI: ", new_uri) ngx.req.set_uri(new_uri, true) -- 设置新的 URI 并应用 end '; #将请求转发到后端服务 proxy_pass http://192.168.1.101; }
# /opt/soft/nginx/sbin/nginx -s reload
# curl 127.0.0.1/api/abc
# tail -f /opt/log/nginx/access.log #当前lua机器查看日志
"GET /api/abc HTTP/1.1" 404 153 "-" "curl/7.61.1"
# tail -f /usr/local/nginx/logs/access.log #登录后台机器查看请求日志,可以看到url被重写请求过来了
"GET /api/v2/api/abc HTTP/1.0" 404 153 "-" "curl/7.61.1"
server_rewrite_by_lua例子介绍:
#例子描述:检查请求的 URI 是否以 /api/v2/ 开头,如果不是,则将原始 URI 拼接到 /api/v2 后面形成新的 URI,将重写后的 URI 应用到当前请求中。
#通过这种方式,可以确保所有请求都以 /api/v2/ 开头,从而实现统一的 API 路径管理
# vim /opt/soft/nginx/main-conf/nginx.conf
location /api { -- 因为server_rewrite_by_lua提示不认,所以更换为rewrite_by_lua_block rewrite_by_lua_block { ngx.log(ngx.ERR, "Original URI: ", ngx.var.uri) -- 打印调试日志 -- 避免重复嵌套,如果URI不以/api/v2/开头,则进入if 块内的逻辑;否则跳过该逻辑 if not string.match(ngx.var.uri, "^/api/v2/") then -- 这一行的作用是将原始URI拼接到/api/v2后面,形成一个新的 URI local new_uri = "/api/v2" .. ngx.var.uri ngx.log(ngx.ERR, "Rewritten URI: ", new_uri) -- 打印重写后的 URI -- 设置新的URI,并应用重写规则,第二个参数true表示强制更新 URI,即使原始 URI 已经被重写过 ngx.req.set_uri(new_uri, true) end } }
# /opt/soft/nginx/sbin/nginx -s reload
# curl 127.0.0.1/api/reource
# tail -f /opt/log/nginx/error.log #因为是例子,所以访问资源不存在,直接看错误日志就行,从例子可看出这个请求是进入到url重写然后再出去重新进入判断然后响应
[error] 168085#0: *730 [lua] rewrite_by_lua(nginx.conf:46):2: Original URI: /api/reource, client: 127.0.0.1, server: localhost, request: "GET /api/reource HTTP/1.1", host: "127.0.0.1" [error] 168085#0: *730 [lua] rewrite_by_lua(nginx.conf:46):5: Rewritten URI: /api/v2/api/reource, client: 127.0.0.1, server: localhost, request: "GET /api/reource HTTP/1.1", host: "127.0.0.1" [error] 168085#0: *730 [lua] rewrite_by_lua(nginx.conf:46):2: Original URI: /api/v2/api/reource, client: 127.0.0.1, server: localhost, request: "GET /api/reource HTTP/1.1", host: "127.0.0.1" [error] 168085#0: *730 open() "/opt/soft/nginx/html/api/v2/api/reource" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /api/reource HTTP/1.1", host: "127.0.0.1"
set_by_lua例子介绍:
set_by_lua和set_by_lua_file区别介绍:
set_by_lua 和 set_by_lua_file :这两个模块都⽤于设置 Nginx 变量。 set_by_lua 通过 inline Lua 代码设置变量的值, set_by_lua_file 则可以通过引⼊ Lua 脚本⽂件来设置变量。这两个模块通常⽤于实现负载的赋值逻辑,如根据请求的 headers 头部信息等进⾏动态变量设置。
先来一个set_by_lua的例子:
location ~ /ftest { #定义一个 Nginx 变量 $input,其初始值为 "Hello, Lua!" set $input "Hello,Lua!"; #通过 Lua 脚本对变量$input使用string.upper 函数将字符串转换为大写,并将结果赋值给变量 $output set_by_lua $output 'return string.upper(ngx.var.input)'; return 200 "Input: $input, Output: $output\n"; }
# curl 127.0.0.1/ftest
Input: Hello,Lua!, Output: HELLO,LUA!
再来一个set_by_lua_block的例子(动态生成变量):
location /time { #用于动态生成变量 $current_time 的值 set_by_lua_block $current_time { #获取当前时间并格式化为标准的日期时间字符串 return os.date("!%Y-%m-%d %H:%M:%S") } #将生成的时间作为响应头的一部分返回 add_header X-Current-Time $current_time; return 200 "The current time is: $current_time\n"; }
再来一个set_by_lua_file的简单例子:
location /multiply { # 定义一个变量 $result 来存储 Lua 脚本的返回值 set $result ""; # 调用外部Lua脚本进行计算,将$arg_number(即 URL参数number的值)作为参数传递给Lua脚本并将返回值赋值给变量$result set_by_lua_file $result '/opt/soft/openresty/nginx/conf/lua/multiply.lua' $arg_number; # 将计算结果返回给客户端 return 200 "Result: $result\n"; }
# \vi /opt/soft/openresty/nginx/conf/lua/multiply.lua
-- 获取第一个参数(即$arg_number的值),并尝试将其转换为数字类型 local number = tonumber(ngx.arg[1]) -- 如果参数无效或无法转换为数字,则返回错误信息 "Invalid input" if not number then return "Invalid input" end -- 将数字乘以2,并将结果转换为字符串类型后返回 return tostring(number * 2)
# curl 127.0.0.1//multiply?number=6
Result: 12
# curl 127.0.0.1//multiply?number=dadssa
Result: Invalid input
access_by_lua_block例子介绍:
#同上,access_by_lua得写法适合简单字符串的形式,access_by_lua_block是后来支持的新特性支持直接编写多行Lua 代码,适合复杂的逻辑场景。更直观、更易维护
#写个简单针对token认证的例子.为了确保所有访问受保护资源的请求都经过身份验证,可以使用 access_by_lua 来实现以下功能
# vim /opt/soft/nginx/main-conf/nginx.conf
location /protected { access_by_lua_block { local token = ngx.req.get_headers()["Authorization"] -- 检查是否有 Authorization 头部 if not token then ngx.status = 401 ngx.header["Content-Type"] = "application/json" ngx.say('{"error": "Unauthorized: Missing token"}') -- ngx.HTTP_UNAUTHORIZED是OpenResty中Nginx Lua模块的一个函数调用,用于立即终止当前请求的处理,并返回指定的 HTTP 状态码给客户端。 ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 假设简单的验证逻辑:token 必须以 "Bearer" 开头 if not string.match(token, "^Bearer") then ngx.status = 402 ngx.header["Content-Type"] = "application/json" ngx.say('{"error": "Unauthorized: Invalid token format"}') ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 进一步的验证逻辑可以在这里添加,例如解码 JWT 并验证签名 ngx.log(ngx.ERR, "Token is valid, allowing request") } #上面都if判断后没有退出自然就流转到了后端响应接口 proxy_pass http://192.168.1.102; }
# /opt/soft/nginx/sbin/nginx -s reload
# curl -I 127.0.0.1/protected #无token请求
HTTP/1.1 401 Unauthorized
# curl -H "Authorization: tokentest" 127.0.0.1/protected #token不规范请求
{"error": "Unauthorized: Invalid token format"}
# curl -H "Authorization: Bearer aabbccdd" 127.0.0.1/protected #正常请求
content_by_lua_block例子介绍:
#content_by_lua是 OpenResty中的一个指令,用于在 Nginx 的content 阶段嵌入Lua 脚本代码,它通常用来动态生成 HTTP响应内容。假设现在要实现一个 RESTful API,用于返回当前服务器的时间。如果用户提供了有效的 API Token,则返回时间;否则返回 401 Unauthorized 错误。
# vim /opt/soft/nginx/main-conf/nginx.conf
location /api/time { content_by_lua_block { -- 获取请求头中的 Authorization 字段 local authorization = ngx.req.get_headers()["Authorization"] -- 检查是否提供了有效的 Token if not authorization or authorization ~= "Bearer" then ngx.status = ngx.HTTP_UNAUTHORIZED ngx.say("Unauthorized: Please provide a valid token") ngx.exit(ngx.HTTP_UNAUTHORIZED) end -- 如果通过了身份验证,返回当前服务器时间 local time = os.date("!%Y-%m-%d %H:%M:%S") ngx.say("Current server time: ", time) } }
# /opt/soft/nginx/sbin/nginx -s reload
# curl -H "Authorization: xxxxyyyy" 127.0.0.1/api/time
Unauthorized: Please provide a valid token
# curl -H "Authorization: Bearer " 127.0.0.1/api/time #注意这是UTC时间,如果想显示北京时区的时间并且服务配置的是北京时区就用local time = os.date("%Y-%m-%d %H:%M:%S")
Current server time: 2025-03-26 07:39:10
header_filter_by_lua_block例子介绍:
# vim /opt/soft/nginx/main-conf/nginx.conf
location / { content_by_lua_block { ngx.say("Hello, World!") } header_filter_by_lua_block { -- 动态设置 X-Custom-Header 的值 if ngx.var.uri == "/" then ngx.header["X-Custom-Header"] = "This is the root path" elseif ngx.var.uri == "/about" then ngx.header["X-Custom-Header"] = "This is the about page" else ngx.header["X-Custom-Header"] = "Unknown path" end } } location /about { content_by_lua_block { ngx.say("About Page") } header_filter_by_lua_block { ngx.header["X-Custom-Header"] = "Custom value for about page" } }
# /opt/soft/nginx/sbin/nginx -s reload
# curl -I http://localhost/
X-Custom-Header: This is the root path
# curl -I http://localhost/dsada
X-Custom-Header: Unknown path
# curl -I http://localhost/about
X-Custom-Header: Custom value for about page
body_filter_by_lua_block例子介绍:
#body_filter_by_lua_block指令允许用户通过 Lua 脚本对HTTP响应体进行修改或处理,主要用途:修改响应内容/分块传输处理/后处理逻辑(在响应发送给客户端之前,执行一些额外的逻辑。例如,压缩响应内容、加密数据或记录日志)
#工作原理: ngx.arg[1]: 表示当前块的响应体数据(字符串类型)/ngx.arg[2]: 表示是否为最后一块数据(布尔值)。修改 ngx.arg[1] 的值会影响最终发送给客户端的响应体内容。body_filter_by_lua_block会影响每个请求的响应阶段,确保脚本逻辑尽量简单以避免性能问题
# vim /opt/soft/nginx/main-conf/nginx.conf
location /body { content_by_lua_block { ngx.say("Hello, this is the original response body.") } body_filter_by_lua_block { -- 检查是否有剩余的响应数据 if ngx.arg[2] then -- 如果是最后一块数据,则添加注释 local last_part = ngx.arg[1] ngx.arg[1] = last_part .. "\n<!-- This is a dynamic footer added by Lua -->" end } }
# curl http://localhost/body
Hello, this is the original response body. <!-- This is a dynamic footer added by Lua -->