基于redis实现限流逻辑

张开发
2026/4/3 12:56:16 15 分钟阅读
基于redis实现限流逻辑
固定窗口计数器在固定时间窗口内记录请求次数如果超过阈值就拒绝否则放行。优点实现简单性能极高实现方式incr命令和expire命令缺点临界突发问题时间窗口固定如果时间窗口设置一分钟次数设置为100那么如果00:59和01:01分别来了100条请求都能成功执行。适合于对窗口平滑度要求不高的请求。redis 实现方式如下实际代码要在lua语言中编写key1:表示业务keycount表示限流次数time: 窗口大小resincr key1;#使用 INCR 命令对 Key 进行加 1 操作。如果 Key 不存在Redis 会先初始化为 0然后加 1返回 1。如果 Key 已存在直接加 1返回当前值。ifres1# 如果返回值不是 1说明 Key 已有过期时间无需重复设置expire key1time# 设置过期时间ifrescountreturn0# 拒绝通行elsereturn1# 允许通行滑动窗口法将时间窗口划分成更小的格子记录每个格子的请求数当前允许的请求书 Math.max(剩余请求数请求数*时间间隔每单位允许最大请求数)实现方式使用zset 记录所有请求的时间戳请求过来的时候先删除score 当前时间 - 时间 间隔的 请求计算当前已存在的请求总数precount, 如果precount 阈值 允许访问并且zscore中加入一条数据否则禁止访问redis 实现方式如下实际代码要在lua语言中编写key1:表示业务key count表示限流次数 time: 窗口大小 ZREMRANGEBYSCORE key1-inf当前时间戳# 清理掉所有发生在‘当前窗口起始时间’之前的旧请求记录。respZCARD key1# 统计已有请求总数ifrespcount ZADD key 当前时间戳uuid# 将当前请求添加进去EXPIRE key1time5# 设置过期时间防止意外情况5表示一个随机数return1# 允许通行elsereturn0# 禁止通行令牌桶法基于hashset实现记录上一次的更新时间和当前token数量。模拟令牌生成和令牌消耗redis 实现方式如下实际代码要在lua语言中编写capility:桶的容量 rate: 每秒生成的速率# 第一步获取key如果key不存在设置keykey的剩余令牌数量设置为最大值即桶的容量, 如果key存在 解析出 current_tokens (当前令牌) 和 last_refill_time (上次时间)resget key1ifkey is null current_tokenscapility last_timecurrent_time# 第二步计算当前允许的令牌数量time_passedcurrent_time - last_time new_tokenstime_passed * rate# 不能超过桶的最大值current_tokenMath.min(capility, new_tokens current_tokens)# 尝试消费令牌ifcurrent_token1SET key1{current_token - 1}: #{current_time}EXPIRE key1300# 设置过期防泄漏return1# 放行elseSET key10: #{current_time}EXPIRE key1300# 设置过期防泄漏return0# 不允许通行漏桶法与令牌桶“允许突发”不同漏桶的核心思想是 “强制平滑”无论流入的水请求有多快流出的水处理请求必须保持恒定的速率。如果水流太快导致桶满了多余的水就会溢出请求被拒绝。redis 实现方式如下实际代码要在lua语言中编写capility:桶的容量 rate: 每秒生成的速率 current_water 当前积水量 key1业务key# 第一步获取key1,如果key1 不存在设置current_water 为0key1的值为0, current_time,若存在则解析值获取当前积水量和上一次的时间戳resget key1ifresnull current_water0;last_timecurrent_time# 第二步计算当前时间积水量如果积水量计算后小于0设置成等于0time_passed(current_time - last_time)/1000(秒)leaked_amounttime_passed * Rate(流出速率)current_waterMath.max(0, current_water - leaked_amount)# 第三步尝试注入新水 (新请求)判断加入新请求后是否会超过桶的容量。ifcurrent_water 1capilitysetkey1#{current_water, current_time}EXPIRE key1300# 设置过期防泄漏return-1;# 请求失败elsesetkey1#{current_water 1, current_time}EXPIRE key1300# 设置过期防泄漏return0;# 请求成功

更多文章