red hmget cannot intercept absence of redis key

352 views
Skip to first unread message

pir...@gmail.com

unread,
Feb 3, 2014, 12:01:18 PM2/3/14
to openre...@googlegroups.com
Hello. I am not Lua a programmer at all.

I want to implement simple redis based load balancing (almost like that on openresty.org > samples > DynamicRoutingBasedOnRedis).

The difference is that i get from redis not single value, but redis hash.

The problem is i cannot catch situation, when wrong key (subdomain) comes from nginx (i want just throw 404 ngx.exit(ngx.HTTP_NOT_FOUND)).

Here is the problem part of code:




local backendInfo, err = red:hmget(subdomain, 'backend', 'dbserver', 'dbpassword', 'staticserver')
if backendInfo[1] == nil then --problem here
 ngx
.log(ngx.ERR, "failed to get backend info by subdomain key: ", err)
 ngx
.exit(ngx.HTTP_NOT_FOUND)
end


i tried:

if not backendInfo

if backandInfo == ''

if backandInfo == nil


and so on.

But without success.

Here is all lua file:

--Выбирает параметры из редис по ключу (субдомен)


local subdomain = ngx.var.subdomain
if not subdomain then
 ngx
.log(ngx.ERR, "no user-agent found")
 
return ngx.exit(400)
end


local redis = require "resty.redis"
local red = redis:new()

local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
 ngx
.log(ngx.ERR, "failed to connect to redis: ", err)
 ngx
.exit(ngx.HTTP_NOT_FOUND)
end


local backendInfo, err = red:hmget(subdomain, 'backend', 'dbserver', 'dbpassword', 'staticserver')
if backendInfo[1] == nil then --problem
 ngx
.log(ngx.ERR, "failed to get backend info by subdomain key: ", err)
 ngx
.exit(ngx.HTTP_NOT_FOUND)
end


if backendInfo == ngx.null then
 ngx
.log(ngx.ERR, "no backendInfo found for key ", key)
 ngx
.exit(ngx.HTTP_NOT_FOUND)
end


--This is for proxy_pass to backend (proxy_pass http://$backend)
ngx
.var.backend = backendInfo[1]


--Theese these passed to php-fpm as headers (proxy_set_header > fastcgi_param)
ngx
.var.dbserver = backendInfo[2]
ngx
.var.dbpassword = backendInfo[3]
ngx
.var.staticserver = backendInfo[4]

Please help.


Yichun Zhang (agentzh)

unread,
Feb 3, 2014, 3:01:15 PM2/3/14
to openresty-en
Hello!

On Mon, Feb 3, 2014 at 9:01 AM, pir.tiv wrote:
> Hello. I am not Lua a programmer at all.
>

Lua is a simple language and the following (short) language manual
should be sufficient:

http://www.lua.org/manual/5.1/manual.html

>
> local backendInfo, err = red:hmget(subdomain, 'backend', 'dbserver',
> 'dbpassword', 'staticserver')
> if backendInfo[1] == nil then --problem here

I guess you should use "ngx.null" instead of "nil" here. Because the
redis "nil" value is represented as ngx.null in lua-resty-redis (not
Lua nil!):

https://github.com/agentzh/lua-resty-redis#methods

And you should test if backendInfo is nil for error handling (like
network failure and etc). Basically:

local backendInfo, err = red:hmget(subdomain, 'backend',
'dbserver',
'dbpassword', 'staticserver')
if not backendInfo then
ngx.log(ngx.ERR, "failed to do hmget: ", err)
return
end
if backendInfo[1] == ngx.null then
...
end

Also, please always check out your nginx's error.log file when you
have problems (and always provide error messages and etc (if any) in
your error.log when reporting problems).

BTW, to debug results returned from lua-resty-redis, it is recommended
to use the lua-cjson library to output the contents in the returned
values. See

https://github.com/agentzh/lua-resty-redis#debugging

This should save you (and us) a lot of time ;)

Regards,
-agentzh

Uspensky Ruslan

unread,
Feb 4, 2014, 10:21:15 AM2/4/14
to openre...@googlegroups.com
Thanks a lot.

Sorry. I read lua-redis documentation https://github.com/agentzh/lua-resty-redis#methods inexactly. Did not understand that lua nil is not the same as nginx.null They are completely different actually.

My litle problem now solved.

Here is final version of lua script with comments. It seems to work for me. Maybe  someone find it useful.




-- OPENRESTY SIMPLE REDIS BASED LOAD BALANCING
--
-- Data required for backend server is redis hash:
--------------------------------------------------
-- hset subdomain-name backend '127.0.0.1:8080' (For NGINX could proxy pass to this ip)
-- hset subdomain-name dbserver 'localhost' --Pass to backend as header (to connect to DB server)
-- hset subdomain-name dbpassword 'somePassword' --Pass to backend as header (to connect to DB server)
-- hset subdomain-name staticserver 'some.cdn.forstaticcontent.com' --Pass to backend as header (URL of CDN)
--------------------------------------------------



local redis = require "resty.redis"


-- Value of variable $subdomain extracted from server_name directive in nginx.conf like that:
--------------------------------------------------
-- server_name ~^(.+)\.site\.com$;
-- set $subdomain $1;
--------------------------------------------------
local subdomain = ngx.var.subdomain


local red = redis:new()

red
:set_timeout(1000) -- 1 second




local redisConnectionEestablished, connectionError = red:connect("127.0.0.1", 6379)
-- If cannot connect to redis server - log connection error to NGINX error.log
-- and stop script execution with HTTP_INTERNAL_SERVER_ERROR (500)
if not redisConnectionEestablished then
 ngx
.log(ngx.ERR, "Connection to redis server failed, returned error: ", connectionError)
 
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end


-- Trying to get hash from redis by key (sumdomain)
local backendInfo, getKeyError = red:hmget(subdomain, 'backend', 'dbserver', 'dbpassword', 'staticserver')
-- First check for internal errors like network failure etc.
-- See below
if not backendInfo then -- Or "if backendInfo == nil" all the same
 ngx
.log(ngx.ERR, "Failed to do get hash value from redis, returned error: ", err)
 
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- After check for internal errors (above) check if redis key realy exists
-- See below
if backendInfo[1] == ngx.null then -- IMPORTANT: Do not confuse nil with ngx.null they are entirely different
 ngx
.log(ngx.ERR, "Failed to get backend server for subdomain ", subdomain)
 
return ngx.exit(ngx.HTTP_NOT_FOUND) -- Return 404 error
end


--Set NGINX $backend variable. Later NGINX will proxy pass to specified backend like that:
--------------------------------------------------
-- proxy_pass http://$backend
--------------------------------------------------
ngx
.var.backend = backendInfo[1]




--Theese passed to backend (like php-fpm) as headers
--------------------------------------------------
-- proxy_set_header $dbserver;
--------------------------------------------------
-- And then backend will:
--------------------------------------------------
 
-- fastcgi_param dbserver $dbserver;
 
--------------------------------------------------

ngx
.var.dbserver = backendInfo[2]
ngx
.var.dbpassword = backendInfo[3]
ngx
.var.staticserver = backendInfo[4]
Reply all
Reply to author
Forward
0 new messages