基于redis lua的一种限流算法

一、构造lua脚本
public String buildLuaScript() {
    StringBuilder lua = new StringBuilder();
    lua.append("local c");
    lua.append("\n redis.call('ZREMRANGEBYSCORE', KEYS[2], 0,  ARGV[1] - ARGV[2])");
    lua.append("\n c = redis.call('ZCARD', KEYS[2])");
    lua.append("\n if tonumber(c) < tonumber(ARGV[3]) then");
    lua.append("\n redis.call('zadd', KEYS[2], ARGV[1], KEYS[1])");
    lua.append("\n end");
    lua.append("\n return c;");
    return lua.toString();
}


二、执行、传参,keys参数为List,其它后面的参数组成一个数组。

String luaScript = buildLuaScript();
        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
Long date = System.currentTimeMillis() / SEC;
ArrayList<String> keys = new ArrayList<>();
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
keys.add(uuid);
keys.add("limit_result_nums");
Number execute = redisTemplateString.execute(redisScript, keys, date, limitExpire, limitSize);
if (execute.longValue() < limitSize) {
    return uuid;
}
return null;


三、解释

keys是数组,KEYS[2]是第2个参数(下标从1开始)。
ARGV[1]当前时间,ARGV[2]限制时间 即,ARGV[1]-ARGV[2] 当前时间-限制时间,即1分钟(限制时间)以前的以limit_result_nums为key的值都删除。
ZREMRANGEBYSCORE key min max 删除key下的得分为min到max之间的值
ZCARD 获取有序集合的成员数
如果成员数小于限制数(limitSize),则增加(ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数),KEYS[2]是key(limit_result_nums),keys[1]是uuid即member,ARGV[1]是date即当前值score。

最后,返回成员数(添加前的)。

如果返回成员数小于限制数,则返回uuid,否则返回空。空就是获取令牌失败,非空则成功。

拿到令牌,才回传结果,以避免回传并发大,让服务器压力过大。

四、复述,总结

窗口为限制时间内,如果限制时间内监测点成员过多,限流。控制参数有两种时间窗口与窗口内成员数量。
要调小并发数,那么增加时间窗口(相当于在线终端变多,更容易达到限制数量)或者减小数量都行。
要调大并发数,那么减小时间窗口(相当于在线终端变小,更难达到限制数量)或者减大数量都行。

文/程忠 浏览次数:0次   2023-11-23 17:03:43

相关阅读


评论:
点击刷新

↓ 广告开始-头部带绿为生活 ↓
↑ 广告结束-尾部支持多点击 ↑