美文网首页
使用redis实现游标式时间限流

使用redis实现游标式时间限流

作者: gao922699 | 来源:发表于2022-08-12 07:23 被阅读0次

常见的限流策略:

访问时incr一个用户标识,给这个值设置一个过期时间,下一次先判断值是否达到限制,没有的话请求完成后incr同时更新过期时间。

这种方法有很多弊端,最明显的就是过期时间。比如限制5秒内只能访问2次,第一次访问后3秒后访问第二次,过期时间又更新成了5秒,这样实际效果就是限制8秒内访问3次,不符合要求。

游标式时间限流策略:

需要一个滑动时间窗口的概念,这个窗口大小是恒定的,跟着时间推移滑动,窗口阈值内请求不超过规定次数;

使用zset(有序集合)结构,key是用户标识,score和value是当前毫秒时间戳,移除窗口时间外的值,判断剩余的记录数是否超过限制。

用到的主要命令:
zadd : 将一个或多个成员元素及其分数值加入到有序集当中
zremrangebyscore : 移除有序集中,指定分数(score)区间内的所有成员
zcard: 计算集合中元素的数量

python实现:

def is_action_allowed(user_id, action_key, period, max_count):
    key = 'hist:%s:%s' % (user_id, action_key)
    now_ts = int(time.time() * 1000)  # 毫秒时间戳
    with client.pipeline() as pipe:  # client 是 StrictRedis 实例
        # 记录行为
        pipe.zadd(key, now_ts, now_ts)  # value 和 score 都使用毫秒时间戳
        # 移除时间窗口之前的行为记录,剩下的都是时间窗口内的:(当前时间-阈值)之前的记录都删掉
        pipe.zremrangebyscore(key, 0, now_ts - period * 1000)
        # 获取窗口内的行为数量
        pipe.zcard(key)
        # 设置 zset 过期时间,避免冷用户持续占用内存
        # 过期时间应该等于时间窗口的长度,再多宽限 1s
        pipe.expire(key, period + 1)
        # 批量执行
        _, _, current_count, _ = pipe.execute()
    # 比较数量是否超标
    return current_count <= max_count

相关文章

网友评论

      本文标题:使用redis实现游标式时间限流

      本文链接:https://www.haomeiwen.com/subject/qinawrtx.html