滑动时间窗口
滑动时间窗口,又称rolling window。为了解决计数器法统计精度太低的问题,引入了滑动窗口算法。比如下图,第二个和第三个组成的时间窗口明显不满足限流要求(单位时间内100个请求数)。
时间窗口拆分为更小粒度,再来看看,前1s仍然满足限流要求
窗口往后滑动一个小格子(0.1s)后 ,触发限流,由此看出,滑动时间窗口可以很大程度的避免计数器统计法带来的精度问题。
代码实现:
class RollintWindowLimit(FlowLimit):
def __init__(self, func):
super(RollintWindowLimit, self).__init__(func)
self.threshold = [] # 滑动窗口
self.limitNum = 10 # 限流次数
self.interval = 1000 # 间隔时间 ms
self.maxRoll = 10 # 滑动小窗口数
self.sleepTime = (self.interval // self.maxRoll) / 1000 # 窗口滑动频率
self.reqCount = 0 # 滑动窗口内请求计数器
self.__init_threshold()
def __call__(self, request, *args, **kwargs):
if self.isLimit():
print("限流中")
return HttpResponse("Error: Flow limit")
ret = self.func(request, self.reqCount, *args, **kwargs)
return ret
def __init_threshold(self):
def roll_window():
"""
小时间窗口,一直往后滑动
:return:
"""
# 插入一个元素
while True:
self.threshold.append(0)
# 判断是否满了,如果满了需要把头去掉
if len(self.threshold) > self.maxRoll:
count = self.threshold.pop(0)
self.reqCount -= count # 更新当前流量情况
time.sleep(self.sleepTime)
t = threading.Thread(target=roll_window, daemon=False)
t.start()
def isLimit(self) -> bool:
if self.reqCount+1 > self.limitNum:
return True
self.reqCount += 1 # 当前访问总数+1
self.threshold[-1] = self.threshold[-1] + 1 # 最近一个时间窗口请求数+1
return False
缺点
滑动时间窗口也不是绝对精准的,这取决于拆分的精度,窗口拆分得越精细,窗口也就越平滑,限流得统计也越精确,但是造成的计算量也越大。具体情况具体分析。