java redis lua

现在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编写的。