用Redis来实现限流也是面试经常被问到的题目,本文将对Redis实现限流进行比较详细的介绍,包括四种限流算法,固定窗口算法、滑动窗口算法、命牌桶算法和漏桶算法。同时,笔者使用golang语言基于redis实现了这四种限流算法,并且进行了相关实验。首先定义限流算法的接口如下
type RateLimiter interface {
TryRequest() (bool, error)
}
任何一个请求,都需要先执行TryRequest,如果返回true,则表示可以访问,否则表示不能访问。
固定窗口算法
所谓的固定窗口算法就是窗口是固定大小的,在窗口所定义的这个时间段内整个系统只允许被访问一定次数。这种算法的优点是实现简单,但缺点在于不灵活。而且在窗口切换的时候会有突刺流量问题,即可能大量的请求都在窗口开始时放行,分布不均衡。固定窗口算法可以使用redis的incr命令实现,将在请求之前让某个key的值增加一,如果结果大于了窗口限定的访问次数,那么就限制访问,否则放行这个请求。相关实现代码如下
type FixedWindowRateLimiter struct {
pool *redis.Pool
key string
limit int64 // 访问上限
}
func NewFixedWindowRateLimiter(limit int64) RateLimiter {
pool := &redis.Pool{
MaxIdle: 8,
MaxActive: 0,
IdleTimeout: 100,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "127.0.0.1:6379")
},
}
key := uuid.New().String()
return &FixedWindowRateLimiter{
pool: pool, key: key, limit: limit}
}
func (r *FixedWindowRateLimiter) TryRequest() (bool, error) {
conn := r.pool.Get()
defer conn.Close()
count, err := redis.Int64(conn.Do("incr", r.key))
if err != nil {
return false, err
}
if count <= r.limit {
return true, nil
}
return false, nil
}
滑动窗口算法
滑动窗口算法在一定程度上克服了固定窗口算法的问题,只要求在当前时刻往前的一段间隔内的请求数量不能超过阈值,可以更加平滑地处理流量。可以使用redis的zset来实现,每一个请求的分数为请求时的时间,基于zrangebyscore命令列出过去一段时间内的请求数量,通过比较阈值决定是否放行请求。可以看到,由于使用zset,过往请求的数据会堆积,但是可以通过定时删除一些比较早的请求数据来缓解这个问题。
在实现上,使用了lua脚本来保证多条redis命令是作为一个原子命令整体执行的,因为在刚开始的版本中,没有这条机制,在实验中发现没有起到正确的限流效果。
type SlideWindowRateLimiter struct {
pool *redis.Pool
key string
limit int64 // 间隔时间内访问上限
interval int64 // 间隔时间,单位ms
}
func NewSlideWindowRateLimiter(limit, interval int64) RateLimiter {
pool := &redis

最低0.47元/天 解锁文章
1203

被折叠的 条评论
为什么被折叠?



