Redis系列-Redis实现限流


用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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值