Nginx Lua块变量设置不生效及Balancer连接失败问题求助
Nginx按请求体字段奇偶性分流的问题及解决
问题背景
需实现Nginx作为负载均衡器,根据请求体中documentNumber字段最后一位数字的奇偶性,转发请求至不同上游服务器。
初始配置问题
最初通过content_by_lua_block设置$target变量,再用if指令执行proxy_pass分流,但Nginx始终读取$target的初始值0,无法获取Lua代码设置的新值。
初始配置代码:
location /payload-test { set $target '0'; content_by_lua_block { ngx.req.read_body() local body = ngx.req.get_body_data() if body then local document = string.sub(body, string.find(body, "documentNumber") + 16, string.find(body, "documentNumber")+30) ngx.print(document) local documentNum = string.match(document, "%d+") ngx.print(documentNum) local lastDigit = string.sub(documentNum, string.len(documentNum)) if math.fmod(lastDigit, 2) == 0 then ngx.var.target = "1" else ngx.var.target = "2" end ngx.print(ngx.var.target) end } #updated block if ($target = "1") { proxy_pass http://port_23301; } if ($target = "2") { proxy_pass http://port_23501; } }
优化后连接拒绝问题
改用access_by_lua_block读取请求体并将结果存入ngx.ctx.target,再通过balancer_by_lua_block设置上游节点。日志显示ngx.ctx.target值正确,但出现连接拒绝错误。
优化后代码:
access_by_lua_block { ngx.req.read_body() local body = ngx.req.get_body_data() if body then local document = string.sub(body, string.find(body, "documentNumber") + 16, string.find(body, "documentNumber")+30) local documentNum = string.match(document, "%d+") local lastDigit = string.sub(documentNum, string.len(documentNum)) if math.fmod(lastDigit, 2) == 0 then ngx.ctx.target = "1" else ngx.ctx.target = "2" end end }
balancer_by_lua_block { local balancer = require('ngx.balancer') ngx.log(1,"Target Variable: ",ngx.ctx.target) if ngx.ctx.target == "1" then local ok, err = balancer.set_current_peer("192.168.72.133", "23301") ngx.log(2,"STATUS: ",ok) ngx.log(2,"ERROR: ",err) if not ok then ok, err = balancer.set_current_peer("192.168.72.133", "23502") end else local ok, err = balancer.set_current_peer("192.168.72.133", "23502") ngx.log(2,"STATUS: ",ok) ngx.log(2,"ERROR: ",err) if not ok then ok, err = balancer.set_current_peer("192.168.72.133", "23301") end end }
Nginx日志:
2024/10/29 09:58:30 [emerg] 3517796#3517796: *30 [lua] p_consult.conf:4):8: Target Variable: 2 2024/10/29 09:58:30 [alert] 3517796#3517796: *30 [lua] p_consult.conf:4):26: STATUS: true 2024/10/29 09:58:30 [alert] 3517796#3517796: *30 [lua] p_consult.conf:4):27: ERRO: nil 2024/10/29 09:58:30 [error] 3517796#3517796: *30 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.72.199, server: 192.168.72.190
解决方案
1. 解决初始配置变量不生效问题
Nginx指令执行顺序是核心原因:content_by_lua_block属于内容阶段,执行时间晚于if指令所在的配置阶段,因此if判断时Lua代码还未执行,只能读取初始值。
正确做法是将变量赋值逻辑放到access阶段(用access_by_lua_block),该阶段早于proxy_pass执行:
location /payload-test { set $target '0'; access_by_lua_block { ngx.req.read_body() local body = ngx.req.get_body_data() if body then local document = string.sub(body, string.find(body, "documentNumber") + 16, string.find(body, "documentNumber")+30) local documentNum = string.match(document, "%d+") local lastDigit = string.sub(documentNum, string.len(documentNum)) -- 字符串转数字后再计算取余 if math.fmod(tonumber(lastDigit), 2) == 0 then ngx.var.target = "1" else ngx.var.target = "2" end end } if ($target = "1") { proxy_pass http://192.168.72.133:23301; } if ($target = "2") { proxy_pass http://192.168.72.133:23502; } }
注意:proxy_pass需写全IP+端口,避免依赖上游定义;lastDigit是字符串,必须转成数字后再用math.fmod,否则会触发Lua错误。
2. 解决优化后连接拒绝问题
从日志看balancer.set_current_peer返回STATUS: true但连接失败,排查方向及修复:
- 上游服务状态检查:用
telnet 192.168.72.133 23301或ss -tulpn | grep 23301验证目标端口是否在监听,确认上游服务是否正常启动。 - 网络权限排查:检查Nginx所在服务器与上游服务器之间的防火墙、安全组规则,确保对应端口的通信被允许。
- balancer阶段配置补全:
balancer_by_lua_block必须配合proxy_pass指定默认上游,否则Nginx可能不会进入该阶段。修正配置:
location /payload-test { # 指定默认上游,可使用任意未占用的本地端口 proxy_pass http://dummy_upstream; access_by_lua_block { ngx.req.read_body() local body = ngx.req.get_body_data() if body then local document = string.sub(body, string.find(body, "documentNumber") + 16, string.find(body, "documentNumber")+30) local documentNum = string.match(document, "%d+") local lastDigit = string.sub(documentNum, string.len(documentNum)) if math.fmod(tonumber(lastDigit), 2) == 0 then ngx.ctx.target = "1" else ngx.ctx.target = "2" end end } balancer_by_lua_block { local balancer = require('ngx.balancer') ngx.log(ngx.INFO, "Target Variable: ", ngx.ctx.target) local host = "192.168.72.133" local port = 23502 if ngx.ctx.target == "1" then port = 23301 end local ok, err = balancer.set_current_peer(host, port) ngx.log(ngx.ALERT, "STATUS: ", ok) ngx.log(ngx.ALERT, "ERROR: ", err) # 主节点失败时切换备用节点 if not ok then local backup_port = (port == 23301) and 23502 or 23301 ok, err = balancer.set_current_peer(host, backup_port) end } } # 定义默认上游,无需实际存在 upstream dummy_upstream { server 127.0.0.1:9999; }
另外,日志中的ERRO: nil是拼写错误,建议修正为ERROR,避免混淆。
额外优化建议
- 请求体解析优化:避免用固定偏移量截取字段,改用
cjson库解析JSON请求体,更可靠:
local cjson = require("cjson") local body_data = cjson.decode(body) local documentNum = body_data.documentNumber local lastDigit = string.sub(tostring(documentNum), -1) -- 直接取最后一位更简洁
- 异常处理:增加对请求体非JSON、
documentNumber不存在等场景的判断,避免Lua报错导致请求失败。
内容的提问来源于stack exchange,提问作者RodrigoLTJ
相关产品推荐
相关产品推荐

