2019独角兽企业重金招聘Python工程师标准>>>

有一些需要限制次数的场景,比如api调用次数限制、在一段时间内只能使用几次的限制,在几秒内、几分钟时间内只能使用几次的限制。简单的实现可以把需要做限制的次数放在redis中,利用redis的特点进行限制。这里只是对本人的一些用法做个简单的小结。

1.在单位时间内只能使用N次的限制

常见于api调用次数限制,时间可以是1秒、1分钟、1小时,1天。其他规则的时间限制,需要自定义。这种的用法相对简单,直接用incr方法就可以实现。这里有个小细节,是先用get方法来获取key的值判断是否达到上限,还是直接用incr的返回值?这里我选择直接获取incr的值,因为先做get判断,之后再做incr操作,如果遇到并发,可能会造成脏读,当然也可以放在事务中实现。

key的构造是一个前缀+对应的时间格式。比如要求是每秒的限制,时间格式就设置为yyyyMMddHHmmss,如果是每1分钟的限制,时间格式就设置为yyyyMMddHHmm。如果是每5秒的限制呢?类似每秒的限制,但是设置时间点的时候,需要再计算,自己定义一个规则,比如取0秒、5秒、10秒,[0,5)秒取0,[5,10)取5,可以[0,5)秒取2,[5,10)取7,总之就是定义规则,判断时间点对应的区间,取区间的代表值,构造最后的key。最后等待key失效的策略清理过期的key。

 /*** * @param key* @param limitSeconds key有效期* @param limitTimes 限制次数* @return -1表示超过限制*/public Long incr(final String key, final int limitSeconds, final int limitTimes) {if(StringUtils.isEmpty(key)) {return 0L;}Long ret = 1L;if (!redisTemplate.hasKey(key)) {redisTemplate.opsForValue().increment(key, 1);redisTemplate.expire(key, limitSeconds, TimeUnit.SECONDS);return ret;}ret = redisTemplate.opsForValue().increment(key, 1);return ret > limitTimes ? -1L : ret;}//调用,省略各种设置,每秒10次的限制
// RedisTest test = new RedisTest();
// if(test.incr("test_20171203170252", 5, 10) < 0){ // key 设置5秒过期
// System.out.println("超过限制");
// }

2.在最近单位时间内只能使用N次的限制

举个例子,最近1分钟内要求限制N次。

关于这个需求,脑子中第一种想到的方法就是利用keys 操作来实现。首先是构造key,直接一个前缀+时间戳,对应的值设置过期时间为1分钟,这样keys 前缀就可以得到1分钟内有效的个数。这种做法很简单,但是效率不高,如果遇到redis集群的情况,效率更低。曾经遇到过一次因为并发高了,用keys 获取数据导致cpu 100%的情况。

  public Long incr2(final String key, final int limitSeconds, final int limitTimes) {if(StringUtils.isEmpty(key)) {return 0L;}long ret = -1L;if ((ret = redisTemplate.keys(key + "*").size()) >= limitTimes) {return -1L;}final String k = key + System.currentTimeMillis();redisTemplate.execute(new RedisCallback<Object>() {public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {redisConnection.setEx(k.getBytes(), limitSeconds, "1".getBytes());return null;}});return ret + 1;}

之后在实际项目的测试环境中,redis使用了cluster,估计是环境的什么配置有问题,用keys 操作的时候,得不到想要的结果。想到redis中还有list这种数据结构,有种预感,应该可以用list实现这种限制,于是看了api,发现llen 还有lrange,突然脑洞一开,想到可以利用这几个命令来实现。通过lpush,把最久的数据放在最右边,通过lrange获取前N个数据,通过ltrim删除过期的数据,于是用list来实现的限制就完成了。没有对应的key时,lpush 并且设置过期时间。设置新的值,再更新一下key的有效期。代码中没做事务,就是简单的实现,有需要再简单加个事务实现就行。

    public Long incr3(final String key, final int limitSeconds, final int limitTimes) {if(StringUtils.isEmpty(key)) {return 0L;}long ret = -1L;long time = System.currentTimeMillis();final String timestamp = "" + time;if (!redisTemplate.hasKey(key)) {redisTemplate.execute(new RedisCallback<Object>() {public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {redisConnection.lPush(key.getBytes(), timestamp.getBytes());redisConnection.expire(key.getBytes(), limitSeconds);return null;}});return 1L;}List<String> list = redisTemplate.opsForList().range(key, 0, limitTimes);int t = 0;// 倒序遍历,查找最后一个没过期的下标for (int i = list.size() - 1; i >= 0; i--) {if (Long.parseLong(list.get(i)) > time - limitSeconds * 1000) {t = i;break;}}// 清除过期的list值redisTemplate.opsForList().trim(key, 0, t);if (t + 1 < limitTimes) {redisTemplate.execute(new RedisCallback<Object>() {public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {redisConnection.lPush(key.getBytes(), timestamp.getBytes());redisConnection.expire(key.getBytes(), limitSeconds);return null;}});ret = t + 2;} else {ret = -1L;}return ret;}

以上就是一些个人的用法总结,遇到其他的再继续完善。

转载于:https://my.oschina.net/u/2274874/blog/1584144

利用redis做次数限制的小结相关推荐

  1. 开启tomcat的apr模式,并利用redis做tomcat7的session的共享。

    更新系统组件 yum -y install readline* xmlto kernel-devel yum* screen vim* psmisc wget lrzsz pcre-devel lib ...

  2. Redis官网——如何利用Redis做服务器集群的分布式锁

    2019独角兽企业重金招聘Python工程师标准>>> 链接:http://redis.io/topics/distlock 原理很简单,一段时间内轮询加锁的key 重点,不同语言的 ...

  3. 利用redis写webshell

    redis和mongodb我之所见 最近自己在做一些个人的小创作.小项目,其中用到了mongodb和redis,最初可能对这二者没有深入的认识. 都是所谓的"非关系型数据库",有什 ...

  4. 【Redis笔记】一起学习Redis | 如何利用Redis实现一个分布式锁?

    一起学习Redis | 如何利用Redis实现一个分布式锁? 前提知识 什么是分布式锁? 为什么需要分布式锁? 分布式锁的5要素和三种实现方式 实现分布式锁 思考思考 基础方案 改进方案 保证setn ...

  5. 【Redis】利用 Redis 实现分布式锁

    技术背景 首先我们需要先来了解下什么是分布式锁,以及为什么需要分布式锁. 对于这个问题,我们可以简单将锁分为两种--内存级锁以及分布式锁,内存级锁即我们在 Java 中的 synchronized 关 ...

  6. 利用redis实现golang的分布式锁

    go使用redis锁 基于Redis的SetNX方法,创建并使用redis锁 曾经在一便文档中,有一句话,引发的我的思考:如果公司内已有可以使用的ZooKeeper.etcd或者Redis集群,那么就 ...

  7. 利用Redis一步步实现优惠券的最终秒杀方案

    订单ID不能采用自增长的原因: 1.规律变化太明显.两天下单的ID的差值,能够计算出商城的订单量: 2.如果采用自增长,订单数据是会不断产生的,到时候要分表,但是每个表的ID都是从0开始增长的,这样I ...

  8. Redis做消息队列,香吗?

    来自:架构师修行之路 菜菜哥,我刚做完了一个订单系统,感觉很简单呀 说说看,大量的订单状态怎么处理的? 我设计的时候可是考虑了这一点,所以用了异步处理,采用了MQ 那用的什么MQ呢,透露一下呗 我用的 ...

  9. spss相关性分析看结果_利用spss做Pearson相关性分析步骤详解

    有蛮多的学生私信老徐问如何利用spss做相关性分析,其实相关性分析应该是spss分析中较为基础的一个功能应用,很多学生可能是因为跨专业或者对统计软件了解较少,在没有经过系统学习的前提下,感觉云里雾里. ...

  10. redis做分布式锁可能不那么简单

    在计算机世界里,对于锁大家并不陌生,在现代所有的语言中几乎都提供了语言级别锁的实现,为什么我们的程序有时候会这么依赖锁呢?这个问题还是要从计算机的发展说起,随着计算机硬件的不断升级,多核cpu,多线程 ...

最新文章

  1. input 选择框改变背景小技巧
  2. 谷歌开源文本生成新方法 LaserTagger,直击 seq2seq 效率低、推理慢、控制差三大缺陷!
  3. 设python中有模块m_关于 Python 命令中的 -m 参数(转帖)
  4. 【运筹学】线性规划 人工变量法 ( 人工变量法案例 | 初始单纯形表 | 检验数计算 | 入基变量 | 出基变量 )
  5. python学习环境安装_python学习系列----环境的安装
  6. DotNetTextBox V2.0 Web Control(ASP.NET2.0 增强型TextBox控件,完全支持AJAX.NET)
  7. python怎么清空屏幕_python如何清屏
  8. mysql技术内幕《读书笔记》
  9. java 常量池溢出_Java方法区和运行时常量池溢出问题分析(转)
  10. 机房监控系统解说—开关传感器篇
  11. 如何搭建maven中,分布式工程
  12. 北京工业大学微型计算机接口技术考试,汇编语言微机原理及接口技术期末试卷含答案...
  13. asp.net 读写 XML 小结
  14. html5播放器硬件加速,视频对比:Mango的HTML 5硬件加速很强?
  15. DT浪潮下,大数据在交通管理中的应用实践
  16. 喝多了,头痛,发篇喝酒坏处的文章,提提醒,适量饮酒
  17. MybatisPlus相关
  18. PTA 7-1 宿舍谁最高?(20分)
  19. vue router连续点击多次路由报错根本原因和解决方法
  20. SCA工具对比分析和应用解读「超全」

热门文章

  1. leetcode - Anagrams
  2. 好的项目需要有好的需求
  3. 锻造完美U盘小偷:活用消息机制
  4. [英语]工作邮件中超实用的100句英文
  5. 变分自编码器(VAE)
  6. Visual Studio Code配置
  7. 19、Flask实战第19天:CSRF攻击与防御
  8. 数组元素的填充与替换、排序和复制
  9. jqueryEasyui常用代码
  10. 我想创业,但不懂技术怎么办