限流器,就是限制用户访问或请求的一种方式,在开发过程中,有些时候不得不去限制用户一直请求,来减少服务器的压力。
那么就来用代码实现一下限流器
这是一个限制访问请求的接口,一个用户30s内只能访问5次,用redis实现
参数:需要一个唯一能识别用户的参数,时间,次数
普通限流器
r = redis.Redis(decode_responses=True)
def pass_window(username,time_zone=30,times=5):
# 获取计数器
count = r.get(username)
if not count:
r.setex(username,time_zone,1)
print("第一次请求")
else:
if int(count) >= times:
print("稍后访问")
return 0
r.incr(username) # redis累加方法
print("继续请求")
return 1
这样一个限流器就完成了,看起来没有什么问题,也确实能限制请求,但是在大量的并发下缺失去了作用, 比如在到期后 开启一个200并发的线程同时去请求,此时redis内是没有记录的,就会导致30s超过5次,甚至更多的请求访问服务器。
为了解决在大量并发下也能够将请求拦截,就可以使用滑动窗口限流器,根据时间来计算请求次数
参数:需要一个唯一能识别用户的参数,时间,次数
滑动窗口限流器
r = redis.Redis(decode_responses=True)
# 滑动窗口
def pass_slide_window(username,time_zone=30,counts=5):
# 获取当前时间戳
now_ts = time.time() * 1000
# 集合 value
value = now_ts
# 算滑动窗口的左边界
old_ts = now_ts - (time_zone * 1000)
r.zremrangebyscore(username,0,old_ts)
# 获取请求次数
count = r.zcard(username)
if not count or count < counts:
# 设置生命周期
r.expire(username,time_zone+1)
# 入库
r.zadd(username,{value:now_ts})
return 1
else:
print("稍后重试")
return 0
这里需要用到redis的有序集合,有序集合里的socre值 我们需要利用起来,进来先获取当前时间戳, *1000是将值扩大,毫秒计算,将当前获取到的时间戳作集合的 value (其实并没有用到), 再计算滑动窗口的左边界值 也需要*1000, 用当前时间减去这个窗口值就得出左边界, 用redis zremrangebyscore()方法根据socre删除左边界之前的值, 再用zcard()方法获取当前集合内的次数,就可以拿出来判断了,最后入集合时需要将当前时间 now_ts传入socre的参数里,这样即使在大量并发下,也可以拦截请求。
最后在需要限制访问的接口中以装饰器的形式加上 限流器方法