redis限流器, redis滑动窗口限流器

限流器,就是限制用户访问或请求的一种方式,在开发过程中,有些时候不得不去限制用户一直请求,来减少服务器的压力。

那么就来用代码实现一下限流器

这是一个限制访问请求的接口,一个用户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的参数里,这样即使在大量并发下,也可以拦截请求。

最后在需要限制访问的接口中以装饰器的形式加上 限流器方法

你可能感兴趣的:(redis,python,tornado)