redis限流和滑动窗口限流
一、redis set nx实现限流
比如我们需要在10秒内限定20个请求,那么我们在setnx的时候可以设置过期时间为当前时间戳+10s,使用set nx 设置20个互不相同的key,当请求的setnx数量达到20时候即达到了限流效果。
当然这种做法的弊端是很多的,比如当统计1-10秒的时候,无法统计2-11秒之内,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题
SETNX 是SET if Not eXists的简写,日常指令是SETNX key value,如果 key 不存在则set成功返回 1,如果这个key已经存在了返回0
底层实现包含三个步骤,用lua脚本保证原子性
1、get:获取这个key
2、比较:比较这个key对应的value是否和自己有关联
3、del:删除这个key
SET lock_key lock_value NX PX 30000
1.lock_key:即锁名称,这个名称应是公开的,在分布式环境中,对于某一确定的公共资源,所有争用方(客户端)都应该知道对应锁的名字。对于 Redis 而言,lock_name 就是 key-value 中的 key,具有唯一性。
2. lock_value:是由客户端生成的一个随机字符串,它要保证在足够长的一段时间内在所有客户端的所有获取锁的请求中都是唯一的,用于唯一标识锁的持有者。
3. NX 表示只有当 lock_key(key) 不存在的时候才能 SET 成功,从而保证只有一个客户端能获得锁,而其它客户端在锁被释放之前都无法获得锁。
4. PX 30000 表示这个锁节点有一个 30 秒的自动过期时间(目的是为了防止持有锁的客户端故障后,无法主动释放锁而导致死锁,因此要求锁的持有者必须在过期时间之内执行完相关操作并释放锁)
二、redis zset 实现延时队列限流
我们可以将请求打造成一个zset数组,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了range方法让我们可以很轻易的获取到2个时间戳内有多少请求
三、滑动窗口实现限流
其本质思想是转换概念,将原本问题的确定时间大小,进行次数限制。转换成确定次数大小,进行时间限制。
https://www.cnblogs.com/taoshihan/p/14132949.html
package utils import "time" var LimitQueue map[string][]int64 var ok bool //单机时间滑动窗口限流法 func LimitFreqSingle(queueName string, count uint, timeWindow int64) bool { currTime := time.Now().Unix() if LimitQueue == nil { LimitQueue = make(map[string][]int64) } if _, ok = LimitQueue[queueName]; !ok { LimitQueue[queueName] = make([]int64, 0) } //队列未满 if uint(len(LimitQueue[queueName])) < count { LimitQueue[queueName] = append(LimitQueue[queueName], currTime) return true } //队列满了,取出最早访问的时间 earlyTime := LimitQueue[queueName][0] //说明最早期的时间还在时间窗口内,还没过期,所以不允许通过 if currTime-earlyTime <= timeWindow { return false } else { //说明最早期的访问应该过期了,去掉最早期的 LimitQueue[queueName] = LimitQueue[queueName][1:] LimitQueue[queueName] = append(LimitQueue[queueName], currTime) } return true }