首页 > 数据库 >使用 Redis 实现限流——滑动窗口算法

使用 Redis 实现限流——滑动窗口算法

时间:2024-04-25 14:45:33浏览次数:25  
标签:窗口 请求 err Redis 限流 滑动

用 Go 语言实现滑动窗口限流算法,并利用 Redis 作为存储后端,可以按照以下步骤进行设计和编码。滑动窗口限流的核心思想是维护一个固定时间窗口,并在窗口内记录请求次数,当窗口滑动时,旧的请求计数被移除,新的请求计数被添加。这里以 Redis 的有序集合(Sorted Set,简称 ZSet)作为数据结构,因为它可以方便地实现时间排序和计数功能。

步骤一:定义滑动窗口参数

确定滑动窗口的几个关键参数:

  • 时间窗口宽度(如:1秒、5分钟等)
  • 允许的最大请求数量(如:每秒100次、每分钟1000次等)

步骤二:选择 Redis 操作

使用 Redis 的有序集合(ZSet),其成员为请求发生的时间戳(Unix 时间戳),分值为请求的计数值(通常初始为1)。ZSet 可以自动按分值排序,便于我们管理滑动窗口内的请求。

步骤三:编写 Go 代码实现限流逻辑

以下是使用 Go 语言和 Redis 实现滑动窗口限流的基本流程:

  1. 初始化 Redis 客户端
    使用 github.com/go-redis/redis/v8 库或其他您熟悉的 Redis 客户端库创建一个 Redis 客户端实例。
import (
    "github.com/go-redis/redis/v8"
)

var rdb *redis.Client

func init() {
    rdb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })
}
  1. 定义限流方法
    创建一个名为 limitRequest 的函数,接收请求的标识符(如 API 路径或用户 ID)和当前时间作为参数。在函数内部执行以下操作:

    a. 计算窗口边界
    根据当前时间计算出滑动窗口的起始和结束时间戳。

    b. 清理过期请求
    使用 ZSet 的范围删除(ZREMRANGEBYSCORE)命令移除窗口起始时间之前的元素,确保窗口内仅包含有效请求。

    c. 累加新请求
    将当前请求的时间戳作为成员加入 ZSet,如果已有相同时间戳的成员,则使用 ZINCRBY 命令递增其分值(表示多次请求在同一时刻)。

    d. 检查请求是否超限
    使用 ZCARD 命令获取窗口内请求总数,与允许的最大请求数比较。若超出限制,则返回限流结果;否则,允许此次请求并返回成功。

func limitRequest(identifier string, now int64, maxRequestsInWindow int) (bool, error) {
    key := fmt.Sprintf("rate_limit:%s", identifier)

    // 计算窗口边界
    windowStart := now - timeWindowWidth
    windowEnd := now

    // 清理过期请求
    if err := rdb.ZRemRangeByScore(ctx, key, "-inf", windowStart).Err(); err != nil {
        return false, err
    }

    // 累加新请求
    pipe := rdb.Pipeline()
    exists, _ := pipe.ZScore(ctx, key, now).Result()
    if exists == nil {
        pipe.ZAdd(ctx, key, &redis.Z{Score: float64(now), Member: now})
    } else {
        pipe.ZIncrBy(ctx, key, 1, now)
    }
    _, err := pipe.Exec(ctx)
    if err != nil {
        return false, err
    }

    // 检查请求是否超限
    count, err := rdb.ZCard(ctx, key).Result()
    if err != nil {
        return false, err
    }
    if count > int64(maxRequestsInWindow) {
        return false, nil // 超限,拒绝请求
    }

    return true, nil // 未超限,允许请求
}
  1. 调用限流方法
    在实际处理请求的逻辑中,先调用 limitRequest 方法判断是否允许此次请求,根据返回值决定是否继续处理请求或返回限流响应。

请注意,以上代码示例假设您已经设置了 Redis 连接信息和相关常量(如 timeWindowWidth)。实际应用中,请根据您的具体需求调整代码,例如添加错误处理、优化性能(如使用 pipeline 或 Lua 脚本)、考虑分布式环境下的锁机制(如 RedLock)等。

此外,滑动窗口限流算法在高并发场景下可能会对 Redis 造成一定压力,尤其是窗口较短且请求量大的情况下。为了提高性能,可以考虑使用布隆过滤器提前过滤掉部分请求,或者结合客户端缓存减少对 Redis 的直接访问。

Redis 发生故障时

使用 Redis 作为限流工具时,确实需要考虑其服务的稳定性。尽管 Redis 是一个高可用、高性能的键值数据库,但在实际生产环境中,任何服务都可能因为各种原因(如硬件故障、网络问题、软件错误等)出现暂时不可用的情况。针对 Redis 崩溃或不可用的情况,可以采取以下几种策略来应对:

  1. 冗余与高可用部署

    • 主从复制:配置 Redis 主从架构,确保数据在多个节点间同步。当主节点崩溃时,可以通过自动或手动切换到已同步数据的从节点继续提供服务。
    • 哨兵模式(Sentinel):使用 Redis Sentinel 提供自动故障检测和主节点切换功能,进一步提升系统的自我恢复能力。
    • 集群模式:部署 Redis 集群,将数据和负载分散在多个节点上,即使部分节点不可用,整个集群仍能继续提供服务。
  2. 客户端容错与重试

    • 连接池管理:在客户端实现连接池管理,当连接失败时能够自动重新建立连接或从池中获取其他可用连接。
    • 重试策略:对于因 Redis 临时不可用导致的失败操作,实施合理的重试策略。比如,短暂延迟后重试(指数退避或固定间隔重试),避免短时间内频繁重试加重 Redis 服务器负担。
    • 降级策略:在 Redis 不可用时,客户端可以暂时执行降级逻辑,如放宽限流条件、允许一定比例的请求通过(牺牲一部分限流效果),或者暂时禁用限流功能,确保服务的基本可用性。
  3. 本地缓存与兜底逻辑

    • 本地计数:在客户端(如应用程序服务器)维持一个本地计数器,用于在短时间内(如几秒钟)进行限流。这样,在 Redis 短暂不可用期间,可以依赖本地计数器进行限流,待 Redis 恢复后,再将本地计数同步回 Redis。
    • 熔断与降级:在客户端或服务治理框架中设置熔断机制,当连续检测到 Redis 服务不可用时,触发熔断状态,直接拒绝部分非关键请求或返回默认值,防止请求堆积导致系统雪崩。
  4. 监控与报警

    • 实时监控:对 Redis 服务的运行状态、性能指标、故障事件进行实时监控,及时发现异常情况。
    • 报警通知:设置警报阈值和通知机制,一旦 Redis 出现故障或性能下降,立即通知运维人员进行干预。

通过上述措施,可以在 Redis 发生故障时降低对限流功能的影响,保障系统的整体稳定性和可用性。同时,应定期对 Redis 集群进行健康检查、性能调优和数据备份,预防潜在问题,提升系统的健壮性。



欢迎关注公-众-号【TaonyDaily】、留言、评论,一起学习。

公众号

Don’t reinvent the wheel, library code is there to help.

文章来源:刘俊涛的博客


若有帮助到您,欢迎点赞、转发、支持,您的支持是对我坚持最好的肯定(_)

标签:窗口,请求,err,Redis,限流,滑动
From: https://www.cnblogs.com/lovebing/p/18157675

相关文章

  • Window下运行Nginx、Redis、ES、RabbitMQ、Consul
    前言有时候需要在Windows下快速安装运行环境,比如Redis、Elasticsearch等,想通过bat命令快速启动。1.Nginx下载地址:https://nginx.org/en/download.html解压2.Redis下载地址:https://github.com/tporadowski/redis/releases解压3.Elasticsearch下载地址:https://www.elas......
  • Redis部署集群、高可用
    1.Redis部署1.1解压redis安装包[root@mongo1~]#tar-zxvfredis-5.0.14.tar.gz-C/usr/local/redis-5.0.14/redis-5.0.14/.gitignoreredis-5.0.14/00-RELEASENOTESredis-5.0.14/BUGSredis-5.0.14/CONTRIBUTINGredis-5.0.14/COPYINGredis-5.0.14/INSTALLredis-5.0.14/......
  • vue路由切换滑动效果 vue页面跳转交互 vue实现动画跳转
    Hello,大家好,我是小编鹏仔,鹏仔一直觉得VUE在H5端路由跳转时交互特别丑,一直想写成那种点击滑动切换类的效果,趁着这两天工作不是很忙就网上搜了下,最终在多个搜索结果中选择了一种方式实现,效果如下图所示,点击路由跳转时是页面是滑动切换。逻辑其实还是很简单的,就是监听在路由切换时加......
  • 6.prometheus监控--监控redis/rabbitmq/mongodb
    1.监控redis1.1安装方式1.1.1二进制源码安装方式参考nginx二进制安装方法redis_exporter下载地址:https://github.com/oliver006/redis_exporter/releases系统服务:cat>/etc/systemd/system/redis_exporter.service<<"EOF"[Unit]Description=PrometheusRedisExport......
  • 28.redis的内存碎片
    1.为什么会有Redis内存碎片?Redis内存碎片产生比较常见的2个原因:1.1Redis存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间。以下是这段Redis官方的原话:Tostoreuserkeys,Redisallocatesatmostasmuchmemoryasthe m......
  • 【Redis】Redis的操作命令(二)——Redis 哈希(HASH)
    Redishash是一个string类型的field(字段)和value(值)的映射表,hash特别适合用于存储对象。当设置一个名为demo的哈希对象时:HSETdemoname"redistutorial"description"redisbasiccommandsforcaching"likes20visitors23000 获取哈希对象语句,如下:HGETALLde......
  • vue tab切换滑动门
    <scriptsetuplang="ts">import{ref}from"vue";constselectName=ref('技术指标及标准')constnacigation=ref(['技术指标及标准','文献著作','报告模版','专家库/项目库',])</script><template&......
  • 多种方法实现Appium屏幕滑动:让用户仿真动作更简单
    简介在移动端应用中,基于简便的原因,用户通常会倾向于使用滑动操作来达到与应用程序中的控件进行交互的,这使得滑动成为自动化测试中频繁使用的关键动作。在Appium中提供了多种方式来实现模拟用户的滑动屏幕动作。滑动操作的场景移动端应用中的滑动场景,大致有如下几种类型:触......
  • docker-desktop使用redis镜像问题
    在学习docker的过程中尝试安装redis提供服务给本地开发环境,由于在windows环境下开发所以使用docker-desktop+wsl模式下运行,具体安装过程这里不展示。1.挂载配置文件与存储目录  刚开始的时候想挂载在docker运行的宿主wsl子系统中,docker-desktop运行时会启动docker-desktop与do......
  • windows下redis设置redis开机自启动
    windows系统下启动redis命令进入redis安装目录cdredis输入redis-server.exeredis.windows.conf启动redis命令,看是否成功可能会启动失败,报[1696]30Jan09:46:07.518#CreatingServerTCPlisteningsocket127.0.0.1:6379:bind:Noerror 报错后:输入redis-cli.exe......