You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:24:54