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

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

什么是限流?

在分布式系统中,流量是一个非常重要的话题。当请求过多时,服务器会承受非常大的压力,并且有可能被拒绝服务。因此,为了保障系统的可用性,通常会对系统流量进行限制,这种机制被称为“限流”。

Redis如何实现限流?

Redis是一个高性能的数据结构服务器,提供了丰富的数据类型和命令,可以实现诸如计数器、锁、缓存和队列等功能。在Redis中,可以使用一些数据类型和命令来实现限流,下面介绍三种实现方式。

1. 时间窗口法

时间窗口法是限流实现中最简单也最常见的一种方式,其基本思路是在单位时间内,对请求进行计数,如果超出了预设的上限,则拒绝该请求。

Redis中,可以使用INCR命令和EXPIRE命令来实现时间窗口法。

def is_action_allowed(user_id, action_key, period, max_count):
    key = f'{user_id}:{action_key}'
    with redis.pipeline() as pipe:
        pipe.incr(key)
        pipe.expire(key, period)
        _, count = pipe.execute()
    return count <= max_count

上述代码中,使用了Redis的管道(pipeline)技术,将多个命令一次性发送给Redis服务器,避免了多次通信导致的性能损失。

2. 漏桶法

漏桶法是一种流量整形(shaping)的算法,其基本思路是将请求放入一个由固定容量的桶中,然后以固定的速度从桶中取出请求进行处理,如果桶中的请求已满,则拒绝该请求。

Redis中,可以使用ZADD命令和ZREMRANGEBYSCORE命令来实现漏桶法。

def is_action_allowed(user_id, action_key, capacity, rate, now):
    key = f'{user_id}:{action_key}'
    with redis.pipeline() as pipe:
        pipe.zadd(key, {now: now})
        pipe.zremrangebyscore(key, 0, now - capacity)
        pipe.zcard(key)
        pipe.expire(key, capacity / rate + 1)
        count = pipe.execute()[2]
    return count <= capacity

上述代码中,使用了Redis的有序集合(sorted set)技术,将请求的时间戳作为有序集合的值,并按时间顺序排序。然后,使用ZREMRANGEBYSCORE命令,删除过期的请求。最后,使用ZCARD命令,获取当前桶中请求的个数,以判断是否超出了桶的容量。

3. 令牌桶法

令牌桶法是一种流量控制(control)的算法,其基本思路是将请求放入一个由固定容量的桶中,然后以固定的速度从桶中取出令牌,只有拥有令牌的请求才能被处理,否则拒绝该请求。

Redis中,可以使用ZADD命令和ZREMRANGEBYSCORE命令来实现令牌桶法。

def is_action_allowed(user_id, action_key, capacity, rate, now, num):
    key = f'{user_id}:{action_key}'
    with redis.pipeline() as pipe:
        pipe.zadd(key, {now: now})
        pipe.zremrangebyscore(key, 0, now - capacity)
        pipe.zrange(key, 0, -1, withscores=True)
        pipe.expire(key, capacity / rate + 1)
        result = pipe.execute()
    stored_time = result[2][0][1]
    count = len(result[2])
    earliest_time = result[2][0][1] if count > 0 else 0
    interval = stored_time - earliest_time
    incr = interval * rate
    if incr < num:
        return False
    with redis.pipeline() as pipe:
        pipe.zremrangebyrank(key, 0, num - 1)
        pipe.execute()
    return True

上述代码中,使用了与漏桶法相同的Redis有序集合技术,并在有序集合中存储请求时间戳。然后,使用ZRANGE命令获取有序集合中的时间戳,并计算出有多少个令牌可以使用。最后,根据令牌数量判断该请求是否允许被处理。

总结

Redis提供了多种数据类型和命令,可以灵活实现限流功能。本文介绍了三种实现方式,分别是时间窗口法、漏桶法和令牌桶法。不同的应用场景可以选择不同的限流算法,以达到最佳的流量控制效果。

示例1:运用时间窗口法实现限流去重

如在博客中,一个IP一分钟只能评论一次,基于这种场景可以采用时间窗口法,代码如下:

def redis_view(request):
    user = request.POST.get('user', None)
    comment = request.POST.get('comment', None)
    if user and comment:
        key = f'comment:{user}'
        with redis.pipeline() as pipe:
            # INCR将值+1,EXPIRE等同于牌桶算法的删除时间
            pipe.incr(key)
            pipe.expire(key, 60)
            count = pipe.execute()[0]
        if count == 1:
            # 保存评论等其他操作
            pass
        else:
            # 拒绝评论等其他操作
            pass
    return HttpResponse('ok')

示例2:运用漏桶法实现限流

如在博客中,用户提交内容过于频繁会导致服务器频繁压力,容易造成崩溃。因此需要在服务器层面上采取限制措施,基于这种场景可以采用漏桶算法,代码如下:

def redis_view(request):
    user = request.POST.get('user', None)
    content = request.POST.get('content', None)
    if user and content:
        now = int(time.time() * 1000)
        key = f'user:content:{user}'
        with redis.pipeline() as pipe:
            # 队列的大小
            pipe.zadd(key, {now:now})
            # 时间范围内过期时间点,超过这个时间所有值都要删除
            pipe.zremrangebyscore(key, 0, now - 60 * 1000)
            # 当前队列的大小
            pipe.zcard(key)
            pipe.expire(key, 60)
            count = pipe.execute()[2]
        if count <= 10:
            # 保存内容等其他操作
            pass
        else:
            # 拒绝提交等其他操作
            pass
    return HttpResponse('ok')

需要注意的是,如果使用很频繁,可以修改60s这个参数的大小,以适应更多的场景。

以上是两个基于时间窗口法和漏桶法的限流示例,仅供参考。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Redis实现限流的三种方式 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • 15个初学者必看的基础SQL查询语句

    15个初学者必看的基础SQL查询语句 1. SELECT语句 SELECT语句使用最频繁,它用于选择需要的字段和数据: SELECT column1, column2, … FROM table_name; 其中 column1,column2等是需要查询的字段,table_name是要从中查询的表名。例如,下面查询students表中的所有数据: SE…

    database 2023年5月21日
    00
  • MySQL数据更新操作的两种办法(数据可视化工具和SQL语句)

    MySQL数据库更新操作是指在已有数据的基础上对数据库中的数据进行修改。常见的两种更新方式为使用数据可视化工具和通过SQL语句手动更新数据库。 一、使用数据可视化工具更新数据库 打开MySQL数据可视化工具,如Navicat、Workbench等; 连接到需要修改的数据库; 选择需要修改的数据表,打开该表的编辑界面; 找到需要更新的数据行,双击该行进行编辑;…

    database 2023年5月22日
    00
  • redis 解决key的乱码问题,并清理详解

    下面是关于如何解决Redis中键名乱码问题以及相应的清理方法的完整攻略。 1. 问题背景 在Redis中,键名可能会出现乱码的情况,这种情况一般是由于Redis服务器和客户端之间采用的不同字符集导致的。如果在Redis键名中包含有非ASCII字符(如中文),在某些情况下,这些字符会被转换为乱码。这样会导致我们无法通过键名查询到相应的键值,造成数据访问的问题。…

    database 2023年5月22日
    00
  • [redis] Zremrangebylex命令移除元素令人困惑不能理解

    在查看一篇滑动窗口实现的限流算法时 , 代码中使用的redis , 就使用到了这个命令 目的是删除集合中范围以外的元素 但是在看文档的时候,文档中的实例令人不能理解 这里的alpha和omega是啥意思 , 完全看不懂 ,也没有地方解释 ,如果是按名称范围删除  从alpha开始删 ,omega不存在 ,那么为啥删到foo就停了 redis 127.0.0.…

    Redis 2023年4月11日
    00
  • Spark SQL 编程初级实践详解

    Spark SQL 编程初级实践详解 介绍 Spark SQL 是 Apache Spark 计算框架下的一种数据处理模块,它提供了类似于 SQL 的语言接口,使得在 Spark 中处理结构化数据变得更加方便和高效。 本文将会详细介绍如何使用 Spark SQL 进行编程,包括数据的加载、SQL 的执行、结果的输出等操作。 数据加载 Spark SQL 支持…

    database 2023年5月22日
    00
  • 小练习-把MySQL数据库中的数据存入redis

    #pymysql、json、redis#1、连数据库,查到数据库里面所有的数据,游标类型要用pymysql.curosrs.DictCour#2、查到所有数据 [ {“id”:1,”passwd”:”49487dd4f94008a6110275e48ad09448″,”username”:”niuhayang”,”is_admin”:1}]#3、循环这个li…

    Redis 2023年4月11日
    00
  • redis debug环境搭建过程详解(使用clion)

    Redis Debug环境搭建过程详解(使用CLion) 概述 Redis Debug环境搭建可以帮助开发人员更方便地定位和解决Redis的问题。本文将介绍使用CLion工具来搭建Redis Debug环境的过程。 前置条件 在进行Redis Debug环境搭建之前,需要满足以下条件: 安装对应平台的CLion工具。 配置好Redis源代码目录。 安装好Re…

    database 2023年5月22日
    00
  • Java多线程事务回滚@Transactional失效处理方案

    Java多线程事务回滚@Transactional失效处理方案攻略 背景 在Java的开发中,我们经常需要处理多线程事务的情况。当某个事务遇到异常需要回滚时,可是@Transactional注解却无法生效,造成数据不一致的风险。本文将介绍一些处理方案,以帮助你在多线程事务中处理好回滚问题。 解决方案 方案一:手动控制事务 对于无法通过@Transaction…

    database 2023年5月21日
    00
合作推广
合作推广
分享本页
返回顶部