Redis深度历险-Redis zset简单限流

本文大部分内容引自《Redis深度历险:核心原理和应用实践》,感谢作者!!!

Redis简单限流,使用zset来实现

需求:指定用户在指定时间范围内最多请求n次

看了这张图是不是会想象到TCP的流量窗口?

需求分析:

1、限流需求中存在一个滑动的时间窗口,而zset的score值可以用来圈定时间窗口,窗口之外的数据都可以删除

2、zset的value需要是一个唯一的值,只需要保证唯一性即可

3、zset的key应该是用户id和行为的组合userid_action

4、同时还需指定用户行为的过期时间为:时间间隔 + 1 (原因:过期时间应该等于时间窗口的长度,再多宽限1s,看需求而定)

5、缺点是内存有可能占用过多,如果用户60s之内不能请求超过100w次,那么就需要在zset中添加100w个值

使用到的命令

ZADD key score1 member1 [score2 member2] #向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZCARD key #获取有序集合的成员数
ZREMRANGEBYSCORE key min max #移除有序集合中给定的分数区间的所有成员

Java示例代码

public class SimpleRateLimiter {
	private Jedis jedis;
	public SimpleRateLimiter(Jedis jedis) {
        this.jedis = jedis;
	}
    /**
    * 思想:zadd(用户id+行为)作为key的多个值,使用(zremrangebysecore key 0 当前时间-时间窗口长度 )来除去时间窗口外的记录
    * zcard统计key中存在的value的个数即是用户请求的次数
    */    
	public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) {
        String key = String.format("hist:%s:%s", userId, actionKey);
        long nowTs = System.currentTimeMillis();
        Pipeline pipe = jedis.pipelined();
        pipe.multi();
        pipe.zadd(key, nowTs, "" + nowTs);
        pipe.zremrangeByScore(key, 0, nowTs - period * 1000);
        Response<Long> count = pipe.zcard(key);
        pipe.expire(key, period + 1);
        pipe.exec();
        pipe.close();
        return count.get() <= maxCount;
	}
    public static void main(String[] args) {
        Jedis jedis = new Jedis();
        SimpleRateLimiter limiter = new SimpleRateLimiter(jedis);
        for(int i=0;i<20;i++) {
        System.out.println(limiter.isActionAllowed("laoqian", "reply", 60, 5));
        }
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值