redis限流
限流是对系统的出入流量进行控制,防止大流量出入,导致资源不足,系统不稳定。
限流系统是对资源访问的控制组件,控制主要的两个功能:限流策略和熔断策略,对于熔断策略,不同的系统有不同的熔断策略诉求,有的系统希望直接拒绝、有的系统希望排队等待、有的系统希望服务降级、有的系统会定制自己的熔断策略,很难一一列举,所以本文只针对限流策略这个功能做详细的设计。
针对找出超出速率阈值的请求这个功能,限流系统中有两个基础概念:资源和策略。
资源 :或者叫稀缺资源,被流量控制的对象;比如写接口、外部商户接口、大流量下的读接口 策略 :限流策略由限流算法和可调节的参数两部分组成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def can_pass_fixed_window(user, action, time_zone=60, times=30): """ :param user: 用户唯一标识 :param action: 用户访问的接口标识(即用户在客户端进行的动作) :param time_zone: 接口限制的时间段 :param time_zone: 限制的时间段内允许多少请求通过 """ key = '{}:{}'.format(user, action) count = redis_conn.get(key) if not count: count = 1 redis_conn.setex(key, time_zone, count) if count < times: redis_conn.incr(key) return True
return False
|
这个方法虽然简单,但有个大问题是无法应对两个时间边界内的突发流量。如上图所示,如果在计数器清零的前1秒以及清零的后1秒都进来了100个请求,那么在短时间内服务器就接收到了两倍的(200个)请求,这样就有可能压垮系统。会导致上面的问题是因为我们的统计精度还不够,为了将临界问题的影响降低,我们可以使用滑动窗口法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| def can_pass_slide_window(user, action, time_zone=60, times=30): """ :param user: 用户唯一标识 :param action: 用户访问的接口标识(即用户在客户端进行的动作) :param time_zone: 接口限制的时间段 :param time_zone: 限制的时间段内允许多少请求通过 """ key = '{}:{}'.format(user, action) now_ts = time.time() * 1000 value = now_ts old_ts = now_ts - (time_zone * 1000) redis_conn.zadd(key, value, now_ts) redis_conn.zremrangebyscore(key, 0, old_ts) count = redis_conn.zcard(key) redis_conn.expire(key, time_zone + 1) if not count or count < times: return True return False
|