现在java项目,经常会需要与redis进行交互,有时一次普通的网络请求会调用10几次,虽然是在内网,长连接,但是还是会发生10几次的网络io,占用多的系统资源,当并发上来的时候,会对整体的网络请求发生一定延迟影响。
其中一种解决方案就是将10次请求的处理做成redis script,处理成一次来处理。但是阿里云的redis集群形式,会对key的分布进行限制,如果不在一个redis node中,就会报错。建议使用hash来存储多个数据,集中在一个node中。通过脚本的方式来执行。redis会保证每个脚本执行时的原子性。
redis脚本采用的lua语言来编写,这个语音轻量处理一般的业务逻辑处理来说已经足够。脚本和java来交互参数传递支持两类参数,一类是KEYS,代表的是redis key list;一类是ARGV 代表的是参数列表,需要传递的特殊参数等。常用的方法有tonumber使字符串转成lua。还支持一些常用的lua lib库。
- base lib.
- table lib.
- string lib.
- math lib.
- debug lib.
- struct lib.
- cjson lib.
- cmsgpack lib.
- bitop lib.
- redis.sha1hex function.
如果传递的java 包装对象,无法以字符串的方式传递时,可以把对象序列化成字符串,再在lua脚本里面通过cjson反序列化成对象,在迭代操作key来做业务处理。同样也会保持原子性的操作。在一定的并发上性能有一定的提升,主要是减少了io的操作次数开销。
-- dongbin.yu balance filter lua
local configLimit = cjson.decode(ARGV[1])
local flag = false
for i,v in ipairs(configLimit) do
-- request
if(v['limitIndicator'] == 1) then
if(v['limitCycle'] == 1) then
local r = tonumber(redis.call('HGET', KEYS[1], 'r'))
if(type(r) ~= 'nil' and r >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 3) then
local hr = tonumber(redis.call('HGET', KEYS[1], 'hr'))
if(type(hr) ~= 'nil' and hr >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 4) then
local mr = tonumber(redis.call('HGET', KEYS[1], 'mr'))
if(type(mr) ~= 'nil' and mr >= v['limitValue']) then
flag = true
break
end
end
elseif(v['limitIndicator'] == 2) then
if(v['limitCycle'] == 1) then
local i = tonumber(redis.call('HGET', KEYS[1], 'i'))
if(type(i) ~= 'nil' and i >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 3) then
local hi = tonumber(redis.call('HGET', KEYS[1], 'hi'))
if(type(hi) ~= 'nil' and hi >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 4) then
local mi = tonumber(redis.call('HGET', KEYS[1], 'mi'))
if(type(mi) ~= 'nil' and mi >= v['limitValue']) then
flag = true
break
end
end
elseif(v['limitIndicator'] == 3) then
if(v['limitCycle'] == 1) then
local c = tonumber(redis.call('HGET', KEYS[1], 'c'))
if(type(c) ~= 'nil' and c >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 3) then
local hc = tonumber(redis.call('HGET', KEYS[1], 'hc'))
if(type(hc) ~= 'nil' and hc >= v['limitValue']) then
flag = true
break
end
elseif (v['limitCycle'] == 4) then
local mc = tonumber(redis.call('HGET', KEYS[1], 'mc'))
if(type(mc) ~= 'nil' and mc >= v['limitValue']) then
flag = true
break
end
end
end
end
return flag
迭代使用的是for i,v in ipairs,lua代码感觉写起来比java要简洁不少。如果使用openresty的话也是需要掌握lua,游戏领域也有不少游戏是采用lua编写的。