api7 / lua-resty-redis-cluster Goto Github PK
View Code? Open in Web Editor NEWThis project forked from steve0511/resty-redis-cluster
Openresty lua client for redis cluster.
License: Apache License 2.0
This project forked from steve0511/resty-redis-cluster
Openresty lua client for redis cluster.
License: Apache License 2.0
I'm trying fetch data from redis cluster via openresty nginx + LuaJIT + this module.
I've tried different enviroment (alpine-fat \ stretch \ with and without LuaRocks \ openresty 1.13 - 1.15) and got same error.
Probably trouble with resty.utils, but same config work perfectly at one of physical servers...
Error message:
lua entry thread aborted: runtime error: /usr/local/openresty/lualib/rediscluster.lua:296: attempt to index a nil value
stack traceback:
/usr/local/openresty/lualib/rediscluster.lua:416: in function 'smembers'
/lua/read.lua:34: in function 'r_members'
/lua/read.lua:70: in function 'gatherNightNodes'
/lua/read.lua:208: in function 'dispatcher'
My last setup:
openresty -V
nginx version: openresty/1.15.8.2
built by gcc 8.3.0 (Alpine 8.3.0)
built with OpenSSL 1.1.1c 28 May 2019
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --with-cc-opt='-O2 -DNGX_LUA_ABORT_AT_PANIC -I/usr/local/openresty/pcre/include -I/usr/local/openresty/openssl/include' --add-module=../ngx_devel_kit-0.3.1rc1 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.15 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.15 --add-module=../rds-csv-nginx-module-0.09 --add-module=../ngx_stream_lua-0.0.7 --with-ld-opt='-Wl,-rpath,/usr/local/openresty/luajit/lib -L/usr/local/openresty/pcre/lib -L/usr/local/openresty/openssl/lib -Wl,-rpath,/usr/local/openresty/pcre/lib:/usr/local/openresty/openssl/lib' --with-pcre --with-compat --with-file-aio --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_xslt_module=dynamic --with-ipv6 --with-mail --with-mail_ssl_module --with-md5-asm --with-pcre-jit --with-sha1-asm --with-stream --with-stream_ssl_module --with-threads --with-stream --with-stream_ssl_preread_module
2) nginx.conf
worker_processes 20;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
lua_shared_dict redis_cluster_slot_locks 100k;
log_format travelata '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time';
server {
listen 80;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.0;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
proxy_set_header Accept-Encoding 'gzip';
access_log /dev/stdout;
error_log /dev/stdout;
location /searchOneThread {
default_type text/html;
content_by_lua_file "/lua/read.lua";
}
location /writeOneThread {
default_type text/html;
content_by_lua_file "/lua/write.lua";
}
location /nginx_status {
stub_status on;
access_log off;
}
}
}
3) content_by_lua_file "/lua/read.lua
local json = require('cjson')
local redis_cluster = require "rediscluster"
local utils = require "resty.utils"
ngx.req.read_body()
local data = ngx.req.get_body_data();
local request = json.decode(data);
local config = {
name = "testCluster", --rediscluster name
serv_list = { --redis cluster node list(host and port),
{ ip = "10.10.6.4", port = 6380 },
{ ip = "10.10.6.4", port = 6381 },
},
--enableSlaveRead = true,
keepalive_timeout = 60000, --redis connection pool idle timeout
keepalive_cons = 1000, --redis connection pool size
connection_timout = 1000, --timeout while connecting
max_redirection = 5, --maximum retry attempts for redirection
}
local red_c = redis_cluster:new(config)
local currentTime = os.time()
--[[
{"criteria":{"operators":[],"hotels":[],"resorts":[],"hotelCategories":[4,7,8,9],"nights":[7,8,9,10,11,12],"priceRangeFrom":6000,"priceRangeTo":1000000,"rootNodes":["20190308-92-2-2-0-0","20190309-92-2-2-0-0","20190310-92-2-2-0-0"],"meals":[],"limit":6000,"toursPerHotelLimit":8}}
]]--
local r_members = function (redis, key)
local res, err = redis:smembers(key)
if not res then
ngx.log("Error redis smembers key: " .. key .. "Error: " .. err)
end
return res
--[[
local co = coroutine.create(function (redis, key)
coroutine.yield(redis:smembers(key))
end)
return coroutine.resume(co, redis, key)
]]--
end
local hGetAll = function (redis, key, currentTime)
res, ok = redis:hgetall(key)
-- res, ok = redis:eval("local r = {} local t = {} for i, d in pairs(redis.call('HGETALL', KEYS[1])) do if(math.fmod(i,2) > 0) then t = {i = d, d = nil} else if tonumber(string.sub(d, 1,10)) > tonumber(ARGV[1]) then r[#r+1] = t.i r[#r+1] = d end t = {i = nil, d = nil} end end return r", 1, key, currentTime)
if not res then
return {}
end
return res;
-- return redis:array_to_hash(res)
end
local buildNodes = function (parentNodeKey, members)
local new = {}
for i,v in pairs(members) do new[i] = parentNodeKey .. "-" .. v end
return new
end;
local getNextLevelSmembers = function (redis, nodeType, nodeKey, criteria)
local members = r_members(redis, nodeType .. nodeKey);
if nodeType == "RootNode_" then
if criteria.nights[1] == nil then
return buildNodes(nodeKey, members)
else
return buildNodes(nodeKey, utils.filter(members, function(el) return utils.list_has(criteria.nights, tonumber(el)) end))
end
elseif nodeType == "NightNode_" then
if criteria.resorts[1] == nil then
return buildNodes(nodeKey, members)
else
return buildNodes(nodeKey, utils.filter(members, function(el) return utils.list_has(criteria.resorts, tonumber(el)) end))
end
elseif nodeType == "ResortNode_" then
return members
end
end
local gatherNightNodes = function (redis, rootNode, criteria)
return getNextLevelSmembers(redis, "RootNode_", rootNode, criteria)
end
local gatherResortNodes = function (redis, nightNode, criteria)
return getNextLevelSmembers(redis, "NightNode_", nightNode, criteria)
end
local gatherPriceNodes = function (redis, resortNode, priceNodesCollection, criteria)
local priceBucketFrom = tonumber(criteria.priceRangeFrom) ~= nil and tonumber(criteria.priceRangeFrom) > 0 and math.floor(criteria.priceRangeFrom / 3000) or nil
local priceBucketTo = tonumber(criteria.priceRangeTo) ~= nil and tonumber(criteria.priceRangeTo) > 0 and math.floor(criteria.priceRangeTo / 3000) or nil
local priceNodes = getNextLevelSmembers(redis, "ResortNode_", resortNode, criteria)
for i,v in pairs(priceNodes) do
if (priceBucketFrom == nil or priceBucketFrom <= tonumber(v)) and (priceBucketTo == nil or priceBucketTo >= tonumber(v)) then
if priceNodesCollection[v] == nil then
priceNodesCollection[v] = {}
end
priceNodesCollection[v][#priceNodesCollection[v]+1] = resortNode .. "-" .. v
end
end
return priceNodesCollection
end
local gatherTours = function(redis, priceNodeCollection, criteria, currentTime)
local tours = {}
local count = 0
local nodeTours;
local needExit;
local cycle = 0;
local filteredTour = false
local totalFiltered = 0
local totalPriceNodesRead = 0
local tour = {
identity = nil,
data = {}
}
redis:init_pipeline()
for pool, priceNodePool in priceNodeCollection do
for i, v in ipairs(priceNodePool) do
totalPriceNodesRead = totalPriceNodesRead + 1
redis:hgetall("PriceNode_" .. v)
-- ngx.say("PriceNode_" .. v)
end
end
local nodeBuckets = redis:commit_pipeline()
for i, nodeTours in pairs(nodeBuckets) do
for key, val in pairs(nodeTours) do
local match = {}
if(math.fmod(key,2) > 0) then
tour = {
identity = val,
data = {}
}
else
for s in val:gmatch("([^;]*);?") do
table.insert(tour.data, s)
end
if tonumber(tour.data[1]) < currentTime then
filteredTour = true
-- totalFiltered = totalFiltered + 1
elseif tonumber(criteria.priceRangeFrom) ~= nill and tonumber(criteria.priceRangeTo) ~= nill and tonumber(criteria.priceRangeFrom) > 0 and tonumber(criteria.priceRangeTo) > tonumber(criteria.priceRangeFrom)
and (tonumber(criteria.priceRangeFrom) > tonumber(tour.data[4]) or tonumber(criteria.priceRangeTo) < tonumber(tour.data[4])) then
filteredTour = true
elseif criteria.operators[1] ~= nil and not utils.list_has(criteria.operators, tonumber(tour.data[6])) then
filteredTour = true
elseif criteria.hotels[1] ~= nil and not utils.list_has(criteria.hotels, tonumber(tour.data[3])) then
filteredTour = true
elseif criteria.hotelCategories[1] ~= nil and not utils.list_has(criteria.hotelCategories, tonumber(tour.data[18])) then
filteredTour = true
elseif criteria.meals[1] ~= nil and not utils.list_has(criteria.meals, tonumber(tour.data[17])) then
filteredTour = true
end
if not filteredTour then
tours[#tours+1] = tour
count = count + 1
else
totalFiltered = totalFiltered + 1
end
tour = {
identity = nil,
data = {}
}
filteredTour = false
end
end
end
-- ngx.say("Total filtered: " .. totalFiltered)
-- ngx.say("Total price nodes read: " .. totalPriceNodesRead)
return tours
end
local dispatcher = function(utils, redis, criteria, currentTime)
local allNightNodes = {}
local allResortNodes = {}
local allPriceNodes = {}
local priceNodesCollection = {}
-- local sortedPriceNodes = {}
for i,v in pairs(criteria.rootNodes) do
allNightNodes = utils.list_extend(allNightNodes, gatherNightNodes(redis, v, criteria) or {})
end
for i,v in pairs(allNightNodes) do
allResortNodes = utils.list_extend(allResortNodes, gatherResortNodes(redis, v, criteria) or {})
end
--for i,v in pairs(allResortNodes) do
--ngx.say(v)
--end
for i,v in pairs(allResortNodes) do
priceNodesCollection = gatherPriceNodes(redis, v, priceNodesCollection, criteria)
end
local toursPerHotels = {}
local tours = gatherTours(redis, utils.sorted(priceNodesCollection, function(a,b) return tonumber(a) < tonumber(b) end), criteria, currentTime)
local sortedTours = {}
for i,v in utils.sorted(tours, function(a,b)
return
tonumber(tours[a].data[4] or 0)
+ tonumber((tours[a].data[7] and tours[a].data[7] ~= '') and tours[a].data[7] or 0)
< tonumber(tours[b].data[4] or 0)
+ tonumber((tours[b].data[7] and tours[b].data[7] ~= '') and tours[b].data[7] or 0)
end) do
if toursPerHotels[v.data[3]] == nil then
toursPerHotels[v.data[3]] = 0
end
if toursPerHotels[v.data[3]] < criteria.toursPerHotelLimit then
sortedTours[#sortedTours+1] = v
toursPerHotels[v.data[3]] = toursPerHotels[v.data[3]] + 1
end
if(#sortedTours >= criteria.limit) then
break
end
end
return sortedTours
end
--local data, error = red_c:smembers("ResortNode_20190308-92-2-2-0-0-11-2162")
--local ok, members = r_members(red_c, "ResortNode_20190308-92-2-2-0-0-11-2162")
--local ok, members = r_members(red_c, "RootNode_" .. request.criteria.rootNodes[1]);
--local members = getNextLevelSmembers(red_c, "RootNode_", request.criteria.rootNodes[1])
--ngx.say(tonumber(request.criteria.priceRangeFrom))
local tours = dispatcher(utils, red_c, request.criteria, currentTime)
--res = red_c:eval("local r = {} local t = {} for i, d in pairs(redis.call('HGETALL', KEYS[1])) do if(math.fmod(i,2) > 0) then t = {i = d, d = nil} else if tonumber(string.sub(d, 1,10)) > tonumber(ARGV[1]) then r[#r+1] = t.i r[#r+1] = d end t = {i = nil, d = nil} end end return r", 1, "PriceNode_20190310-92-2-2-0-0-12-2185-25", currentTime)
--ngx.say(res[2])
red_c:close()
ngx.say(json.encode(tours))
--ngx.say("Total tours: " .. #tours)
--[[
--red_c:init_pipeline()
--red_c:hgetall("PriceNode_20190407-92-2-2-0-0-7-2161-8")
red_c:hgetall("PriceNode_20190407-92-2-2-0-0-8-2162-13")
red_c:hgetall("PriceNode_20190407-92-2-2-0-0-7-2184-13")
local res, err = red_c:commit_pipeline()
if not res then
ngx.log(ngx.ERR, "err: ", err)
else
ngx.say(json.encode(res))
end
--]]
--for i, v in pairs(members) do ngx.print(v .. "\n") end
4) redis_slot.so
gcc redis_slot.c -fPIC -shared -o redis_slot.so
cp redis_slot.so /usr/local/openresty/lualib/
cp rediscluster.lua /usr/local/openresty/lualib/
thank you!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.