用Redis中的zset实现一个限流器
你被限流过吗
我还记得14年抢红米的时候,下面这个图是我最烦的一个图
抢了两个星期,才终于买到了我的第一台小米手机:红米1s。小米商城加入了一个排队的机制,于是我们可以感知到自己被限流了,但大部分服务,比如最近各大电商的抢茅台活动,并没有让我们感知到限流,不管你是手速不够还是被限流,都会给你返回“很遗憾,已经被抢光了”类似的提示。不过确实也没必要让用户感知到这个机制(你看,程序员又想做产品经理的主了),毕竟结果都是一样的。
对于这种火爆的活动,为了保证服务的稳定性,都需要对特定的接口进行限流,用Redis中的zset实现一个限流器该怎么做呢?
如何实现一个限流器
限流器需要实现的功能:在指定时间内,允许一定量的请求通过。
如图所示,横坐标代表了时间,坐标轴上有一个窗口顺着时间的方向,向前移动。窗口最前面的那条线表示的就是“现在”,每进入一个请求,就会在时间轴对应的当下时间处打上一个点。比如我们要实现一个1分钟最多100000次访问的限流器。那么窗口的大小就是1分钟,窗口一直向前移动,我们要保证被窗口框住的请求永远不超过100000个。
使用Redis的zset可以很方便的实现这个功能。主要用到以下几个命令:zremrangeByScore,zcard,zadd。每当一个请求进入,我们就向zset中添加一个member,score值是当前时间的毫秒数。member叫什么不重要,只要保证他不重复就行了。当判断一个请求能否通过的时候,就检测score的值处于“当前时间”和“1分钟之前”之间的member数量,如果超过了限定值,则被限流,否则加入到zset中,给该请求“放行”。为了保证原子性,我们可以选择使用lua脚本来编写逻辑代码。
--KEYS[1]:该次限流对应的key
--ARGV[1]:一分钟之前的时间戳
--ARGV[2]:此时此刻的时间戳
--ARGV[3]:允许通过的最大数量
--ARGV[4]:member名称(随机生成)
redis.call('zremrangeByScore', KEYS[1], 0, ARGV[1])
local res = redis.call('zcard', KEYS[1])
if (res == nil) or (res < tonumber(ARGV[3])) thenredis.call('zadd', KEYS[1], ARGV[2], ARGV[4])return 0
else return 1 end
写一个demo,并发校验,可以看到输出(为了方便测试,我设定的是一分钟最多进入10个请求):
[pool-1-thread-72] INFO blog20210109.Limiter - 进入
[pool-1-thread-16] INFO blog20210109.Limiter - 进入
[pool-1-thread-42] INFO blog20210109.Limiter - 进入
[pool-1-thread-22] INFO blog20210109.Limiter - 进入
[pool-1-thread-91] INFO blog20210109.Limiter - 进入
[pool-1-thread-10] INFO blog20210109.Limiter - 进入
[pool-1-thread-33] INFO blog20210109.Limiter - 进入
[pool-1-thread-83] INFO blog20210109.Limiter - 进入
[pool-1-thread-62] INFO blog20210109.Limiter - 进入
[pool-1-thread-35] INFO blog20210109.Limiter - 进入
[main] INFO blog20210109.Limiter - 一分钟内进入的请求数有:10
彩蛋
最开始我的脚本是这样写的:
redis.call('zremrangeByScore', KEYS[1], 0, ARGV[1])
local res = redis.call('zrangeByScore', KEYS[1], ARGV[1], ARGV[2])
if (res == nil) or (table.getn(res) < tonumber(ARGV[3])) thenredis.call('zadd', KEYS[1], ARGV[2], ARGV[4])return 0
else return 1 end
测验的时候,总是限流失败。本来只允许进入10个,但每次总是会进入15个左右,你看出哪里的问题了吗?
获取完整代码请公众号(Vegout)回复:R01
用Redis中的zset实现一个限流器相关推荐
- Redis中的zset 存储结构(实现)原理
同时满足以下条件时使用ziplist 编码: 元素数量小于128 个 所有member 的长度都小于64 字节 在ziplist 的内部,按照score 排序递增来存储.插入的时候要移动之后的数据. ...
- 使用redis中的zset进行金牌、银牌、铜牌的排序操作
通过使用redis中的zset进行金牌.银牌.铜牌的排序操作 简介: 1. Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员. 2.不同的是每个元素都会关联一个doub ...
- Redis中对ZSet类型的操作命令
写在前面的话:读书破万卷,编码如有神 -------------------------------------------------------------------- ------------ ...
- zset获取指定score_7、Redis中对ZSet类型的操作命令
写在前面的话:读书破万卷,编码如有神 -------------------------------------------------------------------- ------------ ...
- Redis中的zset原理以及用Java实现跳跃表
准备工作 先在Redis官网下载最新的稳定版本6.2.按照官网给出的安装指南到Linux服务器上安装. zadd调用过程 redis/src/server.c 文件中定义了所有命令运行要调用的方法.z ...
- Redis中的zset 有序集合
存储类型 sorted set,有序的set,每个元素有个score. score 相同时,按照key 的ASCII 码排序. 数据结构对比: 数据结构 是否允许重复元素 是否有序 有序实现方式 列表 ...
- redis中的zset
1.组成结构 跳跃表结构 2.接口API zslCreate:初始化zskipList的层数为1,长度为0,尾指针为NULL,创建zskiplistNode哨兵结点,结点层数为32,每层的前向指针为N ...
- 【承】Redis 原理篇——关于 Redis 中的事务
前言 关于 Redis 的"起承转合",我前面已经用五个篇章的长度作了一个 Redis 基础篇--"起"篇的详细阐述,相信大家无论之前有没有接触过 Redis, ...
- redis 中 set 和 hset 有什么不同,什么时候使用 hset 什么时候使用set?
转载:https://blog.csdn.net/wab719591157/article/details/73379844 redis 中存数据时,到底什么时候用 hset 相比于 set 存数据 ...
最新文章
- 中科院院士指出“万物互联”只是开始,“万物控制”是物联网下一个挑战
- UESTC_秋实大哥下棋 2015 UESTC Training for Data StructuresProblem I
- UITextField属性
- C++ explicit关键字应用方法详解
- 如何反编译APK文件
- 无意中发现的MSDN软件下载网站
- [201511118]发生系统错误86.txt
- Android_自适应布局
- python设计选择题代码_《Python程序的设计》试题库完整
- matlab 螺纹,关于螺纹边缘提取及参数测量
- 恒指期货高手背后的辛酸付出谁又能懂
- 天龙八部服务器端 修改缥缈峰副本,天龙八部手游缥缈峰副本怎么过 缥缈峰副本过关攻略...
- H5 网络版坦克大战
- 英特尔、高通等多家科技巨头禁止员工与华为交流!
- Java验证中文汉字、英文字母、标点符号一个字符占多少字节
- Android中铃声的使用
- 心电图心电轴怎么计算_心电轴度数计算表
- vba文字型的值转换成数值类型
- HTML5绘制国际象棋,如何用纯CSS实现一副国际象棋
- element dialog 设置背景色
热门文章
- BusyCal for Mac(任务日历工具)
- IBM 开源J9 JVM
- 爱剪辑 服务器维护,爱剪辑联网检测失败怎么办-修复爱剪辑联网检测失败的方法 - 河东软件园...
- 书单|如何摆脱“穷人思维”的陷阱?
- 实战1-数据项目分析流程
- AndroidStudio初体验
- matlab 轴系校中,轴系校中简介.ppt
- 去哪儿网数据同步平台技术演进与实践
- Java 设计模式总结及六大设计原则
- 三个数差的平方公式推导过程_平方差公式证明推导过程及运用详解(数学简便计算方法之一)...