详解Redis实现限流的三种方式


Posted in Redis onApril 27, 2021

 面对越来越多的高并发场景,限流显示的尤为重要。

      当然,限流有许多种实现的方式,Redis具有很强大的功能,我用Redis实践了三种的实现方式,可以较为简单的实现其方式。Redis不仅仅是可以做限流,还可以做数据统计,附近的人等功能,这些可能会后续写到。

第一种:基于Redis的setnx的操作

      我们在使用Redis的分布式锁的时候,大家都知道是依靠了setnx的指令,在CAS(Compare and swap)的操作的时候,同时给指定的key设置了过期实践(expire),我们在限流的主要目的就是为了在单位时间内,有且仅有N数量的请求能够访问我的代码程序。所以依靠setnx可以很轻松的做到这方面的功能。

     比如我们需要在10秒内限定20个请求,那么我们在setnx的时候可以设置过期时间10,当请求的setnx数量达到20时候即达到了限流效果。代码比较简单就不做展示了。

    具体的setnx用法可以参照我另一篇博客  RedisTemplate下Redis分布式锁引发的系列问题

   当然这种做法的弊端是很多的,比如当统计1-10秒的时候,无法统计2-11秒之内,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题

第二种:基于Redis的数据结构zset

     其实限流涉及的最主要的就是滑动窗口,上面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。

    而我们如果用Redis的list数据结构可以轻而易举的实现该功能

    我们可以将请求打造成一个zset数组,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了range方法让我们可以很轻易的获取到2个时间戳内有多少请求

    代码如下

public Response limitFlow(){
        Long currentTime = new Date().getTime();
        System.out.println(currentTime);
        if(redisTemplate.hasKey("limit")) {
            Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime -  intervalTime, currentTime).size();        // intervalTime是限流的时间 
            System.out.println(count);
            if (count != null && count > 5) {
                return Response.ok("每分钟最多只能访问5次");
            }
        }
        redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime);
        return Response.ok("访问成功");
    }

   通过上述代码可以做到滑动窗口的效果,并且能保证每N秒内至多M个请求,缺点就是zset的数据结构会越来越大。实现方式相对也是比较简单的。

第三种:基于Redis的令牌桶算法

    提到限流就不得不提到令牌桶算法了。具体可以参照度娘的解释  令牌桶算法

    令牌桶算法提及到输入速率和输出速率,当输出速率大于输入速率,那么就是超出流量限制了。

    也就是说我们每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。

    依靠上述的思想,我们可以结合Redis的List数据结构很轻易的做到这样的代码,只是简单实现

    依靠List的leftPop来获取令牌

// 输出令牌
public Response limitFlow2(Long id){
        Object result = redisTemplate.opsForList().leftPop("limit_list");
        if(result == null){
            return Response.ok("当前令牌桶中无令牌");
        }
        return Response.ok(articleDescription2);
    }

   再依靠Java的定时任务,定时往List中rightPush令牌,当然令牌也需要唯一性,所以我这里还是用UUID进行了生成

// 10S的速率往令牌桶中添加UUID,只为保证唯一性
    @Scheduled(fixedDelay = 10_000,initialDelay = 0)
    public void setIntervalTimeTask(){
        redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
    }

    综上,代码实现起始都不是很难,针对这些限流方式我们可以在AOP或者filter中加入以上代码,用来做到接口的限流,最终保护你的网站。

    Redis其实还有很多其他的用处,他的作用不仅仅是缓存,分布式锁的作用。他的数据结构也不仅仅是只有String,Hash,List,Set,Zset。有兴趣的可以后续了解下他的GeoHash算法;BitMap,HLL以及布隆过滤器数据(Redis4.0之后加入,可以用Docker直接安装redislabs/rebloom)结构。目前我也已经把这些Redis的其他作用都用在了我自己的博客网站(前端技术较渣,没时间研究怎么把文章爬虫下来用MarkDown展示,有会的大佬可以指教下)。

到此这篇关于详解Redis实现限流的三种方式的文章就介绍到这了,更多相关Redis 限流内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
详解Redis瘦身指南
May 26 Redis
深入理解redis中multi与pipeline
Jun 02 Redis
你真的了解redis为什么要提供pipeline功能
Jun 22 Redis
厉害!这是Redis可视化工具最全的横向评测
Jul 15 Redis
Redis Cluster集群动态扩容的实现
Jul 15 Redis
Redis入门教程详解
Aug 30 Redis
Redis集群新增、删除节点以及动态增加内存的方法
Sep 04 Redis
Redis读写分离搭建的完整步骤
Sep 14 Redis
Springboot/Springcloud项目集成redis进行存取的过程解析
Dec 04 Redis
基于Redis6.2.6版本部署Redis Cluster集群的问题
Apr 01 Redis
使用Redis做预定库存缓存功能
Apr 02 Redis
Redis 报错 error:NOAUTH Authentication required
May 15 Redis
在K8s上部署Redis集群的方法步骤
Redis持久化与主从复制的实践
浅谈Redis在直播场景的实践方案
Apr 27 #Redis
redis限流的实际应用
Apr 24 #Redis
Redis安装启动及常见数据类型
redis配置文件中常用配置详解
Apr 14 #Redis
Redis遍历所有key的两个命令(KEYS 和 SCAN)
Apr 12 #Redis
You might like
FCKeditor添加自定义按钮
2008/03/27 PHP
PHP目录与文件操作技巧总结(创建,删除,遍历,读写,修改等)
2016/09/11 PHP
PHP chop()函数讲解
2019/02/11 PHP
PHP 枚举类型的管理与设计知识点总结
2020/02/13 PHP
Aliyun Linux 编译安装 php7.3 tengine2.3.2 mysql8.0 redis5的过程详解
2020/10/20 PHP
jquery 问答知识整理
2010/02/11 Javascript
javascript是怎么继承的介绍
2012/01/05 Javascript
让input框实现类似百度的搜索提示(基于jquery事件监听)
2014/01/31 Javascript
javascript setinterval 的正确语法如何书写
2014/06/17 Javascript
使用js画图之画切线
2015/01/12 Javascript
jquery在ie7下选择器的问题导致append失效的解决方法
2016/01/10 Javascript
js实现的下拉框二级联动效果
2016/04/30 Javascript
jQuery和hwSlider实现内容响应式可触控滑动切换效果附源码下载(二)
2016/06/22 Javascript
JS简单实现浮动窗口效果示例
2016/09/07 Javascript
JS判断是否为JSON对象及是否存在某字段的方法(推荐)
2016/11/29 Javascript
angularJS+requireJS实现controller及directive的按需加载示例
2017/02/20 Javascript
vue 解决移动端弹出键盘导致页面fixed布局错乱的问题
2019/11/06 Javascript
Vue.js 无限滚动列表性能优化方案
2019/12/02 Javascript
《javascript设计模式》学习笔记三:Javascript面向对象程序设计单例模式原理与实现方法分析
2020/04/07 Javascript
浅谈js中的attributes和Attribute的用法与区别
2020/07/16 Javascript
vue-以文件流-blob-的形式-下载-导出文件操作
2020/08/07 Javascript
js闭包和垃圾回收机制示例详解
2021/03/01 Javascript
[01:01:18]VP vs NIP 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python玩转Excel的读写改实例
2019/02/22 Python
Python 循环终止语句的三种方法小结
2019/06/24 Python
python脚本开机自启的实现方法
2019/06/28 Python
在pytorch中实现只让指定变量向后传播梯度
2020/02/29 Python
解决Keras自带数据集与预训练model下载太慢问题
2020/06/12 Python
python实现企业微信定时发送文本消息的示例代码
2020/11/24 Python
使用Canvas操作像素的方法
2018/06/14 HTML / CSS
全球知名旅游社区法国站点:TripAdvisor法国
2016/08/03 全球购物
HQhair美国/加拿大:英国化妆品、美容及美发产品商城
2019/04/15 全球购物
乡镇消防安全责任书
2014/07/23 职场文书
六一文艺汇演开幕词
2015/01/29 职场文书
2016年师德师风学习心得体会
2016/01/12 职场文书
vue3使用vue-router的完整步骤记录
2021/06/20 Vue.js