0

I'm setting nginx as a load balance for web applications and in one of them we have to filter the last character of a document field to decide where to send that request. So I decided to set a flag "target" on the nginx location block to load/change it inside a lua script block with values 1 or 2 so I can use an IF inside the location block to proxy_pass to different upstreams.

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; } } 

The code itself works fine on lua, when I print the data, it shows the right info on Postman. The problem is that when I try to return the value of $target, nginx returns only the initial value "0" and not 1 or 2, set by lua block.


After the sugestions, i split the code and created an access_by_lua_block, to be able to get the payload, extract the document and store it on ngx.ctx.target variable:

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 } 

Then, on the upstream, i created a balancer_by_lua_block?

 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 } 

The nginx log says:

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 Answer 1

0

The return directive is a part of ngx_http_rewrite_module, which means it's executed before the access phase (see a diagram below), and, according to the nginx docs, it “Stops processing and returns the specified code to a client”, that is, the access_by_lua_block is not executed at all, see this issue.

rewrite_by_lua_block won't work either, as “this handler always runs after the standard ngx_http_rewrite_module”, that is, with return it also doesn't have a chance to be executed.

set_by_lua_block runs before return, but the cosocket API is disabled in the context of this directive, which means ngx.req.read_body() won't work (API disabled in the context of set_by_lua* [...] in function 'read_body').

I would get rid of return and use content_by_lua_block to read the request body and produce the response.

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the info, i think i got a bit better understanding of what happened. In this case, i only used the return to see if the value updated before inserting the proxy_pass block... I did try to switch for content_by_lua_block and added the proxy_pass as updated on the first post... But it´s still not updating the variables...
As you can see on the diagram ("content generated by?"), content_by_lua_block and proxy_pass are mutually exclusive. In your configuration, proxy_pass is executed and content_by_lua_block is simply ignored. If you need to programmatically dispatch requests to upstreams based on the request content, there is balancer_by_lua_block. Check the docs and this answer.
Thanks for all the help, finally feels like getting somewhere... I split the code, stored the target in a ngx variable, was able to get it in the balancer_by_lua_block on the upstream and manage to split the traffic between the 2 servers... I updated the question with the second block... The problem now is that when i drop the second server, the "err" variable seems to not identify the connection refused error and "ok" variable is always true... So it never sends the request for the first server in case of faillure... I tried to log the values so i can see whats happening.
Hi, i think i just figure out how to do it and it worked... i checked nginx lua documentation and found a socket method... I implemented a ngx.socket.tcp() check on the ip / port and this one identifies the connection refused... With this i can change the target and redirect to the one working... Thanks for all the help, i really got to understand better how to do stuff, the phases, etc....

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.