(四)redis实现限流的两种方式

方式一:滑动窗口

实现原理:利用zset的score值来圈定这个时间窗口。每次请求过来,都会使用zset的范围查询统计总数,这个范围就是(当前时间)到(当前时间-滑动窗口时间),然后将总数和限流次数进行比较,从而决定是否限流

那这个 zset 的 value 填什么比较合适呢?它只需要保证唯一性即可,用 uuid 会比较浪费空间,那就改用毫秒时间戳吧。

    public boolean limitFlow(String redisKey){
        int targetNum = 5; //限流次数
        long intervalTime = 60 * 1000; //时间窗口为60秒
        long currentTime = System.currentTimeMillis();
        //查询当前时间往前找 intervalTime限流时间这个范围有多少个数据
        Integer count = redisTemplate.opsForZSet().rangeByScore(redisKey, currentTime-intervalTime, currentTime).size();   
        if (count != null && count > targetNum) {
            //超出限流范围
            return false;
        }
        //没有超出限流范围 则将当前值放入zset中并返回成功
        redisTemplate.opsForZSet().add(redisKey,currentTime,currentTime);
        return true;
    }

方式二:漏斗限流(funnel)

漏斗的容量是有限的,如果将漏嘴堵住,然后一直往里面灌水,它就会变满,直至再也装不进去。如果将漏嘴放开,水就会往下流,流走一部分之后,就又可以继续往里面灌水。如果漏嘴流水的速率大于灌水的速率,那么漏斗永远都装不满。如果漏嘴流水速率小于灌水的速率,那么一旦漏斗满了,灌水就需要暂停并等待漏斗腾空。

但是有个问题:java实现的话无法保证整个操作的原子性。因为涉及到从redis中取值,内存中计算,再放回到redis中。那就意味着需要加锁。但是加锁可能失败,如果重试就影响性能,不重试影响用户体验,正在艰难选择的时候Redis-Cell出现了。

Redis4.0提供了一个限流模块,就是Redis-Cell。该模块使用了漏斗算法并且提供了原则的限流指令

该模块只有1条指令cl.throttle,它的参数和返回值都略显复杂,接下来让我们来看看这个指令具体该如何使用。

> cl.throttle laoqian:reply 15 30 60 1
                      ▲     ▲  ▲  ▲  ▲
                      |     |  |  |  └───── need 1 quota (可选参数,默认值也是1)
                      |     |  └──┴─────── 30 operations / 60 seconds 这是漏水速率
                      |     └───────────── 15 capacity 这是漏斗容量
                      └─────────────────── key laoqian

上面这个指令的意思是允许「用户老钱回复行为」的频率为每 60s 最多 30 次(漏水速率),漏斗的初始容量为 15,也就是说一开始可以连续回复 15 个帖子,然后才开始受漏水速率的影响。我们看到这个指令中漏水速率变成了 2 个参数,替代了之前的单个浮点数。用两个参数相除的结果来表达漏水速率相对单个浮点数要更加直观一些。

> cl.throttle laoqian:reply 15 30 60
1) (integer) 0   # 0 表示允许,1表示拒绝
2) (integer) 15  # 漏斗容量capacity
3) (integer) 14  # 漏斗剩余空间left_quota
4) (integer) -1  # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2   # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)

在执行限流指令时,如果被拒绝了,就需要丢弃或重试。cl.throttle 指令考虑的非常周到,连重试时间都帮你算好了,直接取返回结果数组的第四个值进行 sleep 即可,如果不想阻塞线程,也可以异步定时任务来重试。

猜你喜欢

转载自blog.csdn.net/lss446937072/article/details/109684683
今日推荐