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

yizhihongxing

详解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日

相关文章

  • ruby专题

    Ruby专题攻略 Ruby 是一种简单、优雅且易于学习的编程语言。被广泛应用于Web开发、脚本编写、数据分析等领域。在本篇攻略中,我们将会全面深入地介绍 Ruby 编程语言。 一、Ruby 的基础语法 变量定义 Ruby 中的变量定义使用变量名和变量值,变量名必须以字母或下划线开头。 示例: name = "ruby" age = 10 …

    database 2023年5月22日
    00
  • Windows下mysql 5.7 设置区分大小写(敏感),设置默认编码 utf8mb4

    下面是针对在Windows系统下MySQL 5.7设置区分大小写和设置默认编码为utf8mb4的完整攻略。 步骤一:修改配置文件 默认情况下,Windows下安装的MySQL 5.7版本的配置文件位于 C:\ProgramData\MySQL\MySQL Server 5.7\my.ini,我们需要修改这个文件。 1.1 打开 my.ini 文件,在 [my…

    database 2023年5月22日
    00
  • 十七个经典问答让您更了解虚拟主机技术

    十七个经典问答让您更了解虚拟主机技术 什么是虚拟主机? 虚拟主机是一种共享托管服务器上的网站托管解决方案。虚拟主机通常将相同的物理服务器上不同用户之间隔离,实现一台服务器上托管多个网站的共享托管模式。 为什么使用虚拟主机? 虚拟主机通常比独立服务器成本更低,适用于个人博客或小型业务。虚拟主机还提供了许多现成的解决方案,例如自动安装程序和管理面板来帮助用户简化…

    database 2023年5月22日
    00
  • MySQL中Case When用法及说明

    MySQL中的CASE WHEN语句是一种非常有用的控制流语句,它允许我们根据条件表达式的结果来执行不同的操作。在本文中,我将详细讲解CASE WHEN的用法及说明。 基本语法 CASE WHEN语句的一般格式如下: CASE WHEN condition1 THEN result1 WHEN condition2 THEN result2 … WHEN…

    database 2023年5月22日
    00
  • ubuntu20.04 安装 MySQL5.7过程记录

    下面是 “ubuntu20.04 安装 MySQL5.7过程记录” 的完整攻略。 准备工作 在ubuntu20.04的命令行终端中,输入以下命令更新 apt 包管理工具: $ sudo apt update && sudo apt upgrade -y 安装 MySQL5.7 依赖项 $ sudo apt install mysql-serv…

    database 2023年5月22日
    00
  • MySQL日期加减函数详解

    MySQL日期加减函数详解 MySQL提供了强大的日期加减函数,可以对数据库中的日期进行加减操作。在本文中,我们将详细讲解MySQL日期加减函数的使用方法。 DATE_ADD函数 DATE_ADD函数可以对指定的日期进行加减操作,并返回计算后的日期。 SELECT DATE_ADD(‘2022-01-01’, INTERVAL 1 MONTH); 运行以上S…

    database 2023年5月22日
    00
  • MySQL最佳实践之分区表基本类型

    MySQL最佳实践之分区表基本类型 分区表是MySQL5.1之后提供的表类型,它将一张大表分割成多个小表,可以大大提高查询效率。下面是分区表的几种基本类型: RANGE分区 根据指定的列值区域进行分区,语法如下: CREATE TABLE 表名( 列名 数据类型, … ) PARTITION BY RANGE(列名)( PARTITION 子表1 VAL…

    database 2023年5月21日
    00
  • Redis 排行榜 相同分数根据时间优先排行

        版权声明:本文为博主原创文章,未经博主允许不得转载。 1. 需求     Redis 提供了按分数进行排序的有序集合。 比如在游戏里面,比如战斗力排行,充值排行,用默认的Redis 实现就可以达到需求。     但是,比如等级排行,大家都是30级,谁先到30级谁第一。Redis 默认实现是,相同分数的成员按字典顺序排序(0 ~9 , A ~Z,a ~…

    Redis 2023年4月12日
    00
合作推广
合作推广
分享本页
返回顶部