限流的目的
保证系统可用的一个重要手段,和消息队列相似,都是防止超负荷的流量直接打在服务上造成故障,但又有所不同,消息队列其中一个作用是削峰,但是带来的副作用是异步,业务流程需要配合,而限流则是对请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。
有几种常见的限流手段
- 固定时间窗口限流
- 滑动时间窗口限流
- 漏桶限流
- 令牌桶限流
固定窗口限流
本文主要说明固定窗口限流,首先需要先理解一个概念:时间窗口。简单来说就是把时间分成一个个小格子,而固定窗口限流的作用就是限制单位串口内的请求数,防止系统超载。如下图,相同颜色的格子组成1s,假设单位时间(1s)内限流数是100,那么下图的结构是初步满足需求的。
分析问题主要的要素有:当前时间窗口内累计请求数、单位时间窗口允许的最大请求数、单位时间大小。
主要的操作有:
判断是否限流:判断单位时间内累计请求数是否大于最大请求数。
更新时间窗口:每单位时间移动一个时间窗口。
class CountLimit(FlowLimit):
def __init__(self, func):
super(CountLimit, self).__init__(func)
self.reqCounter = 0 # 当前窗口流量
self.limitNum = 100 # 限流次数
self.interval = 1000 # 限流时间间隔(ms)
self.currentTime = self.getNow() # 当前时间
def __call__(self, request, *args, **kwargs):
if self.isLimit():
raise Exception("flow limit")
ret = self.func(request, self.reqCounter, *args, **kwargs)
return ret
def isLimit(self) -> bool:
"""
判断是否限流
:return:
"""
self.__roll_window()
if (self.reqCounter+1 > self.limitNum):
print("flow limit")
return True
self.reqCounter += 1
return False
def __roll_window(self):
"""
更新到最新时间窗口
:return:
"""
now = self.getNow()
if (now > self.interval+self.currentTime):
self.reqCounter = 0
self.currentTime = now
缺点
固定时间窗口实现起来简单,但是缺陷是误差太大,如下图,第二和第三个0.5s组成的1s明显超过限流数了。滑动时间窗口限流算法可以缓解这一问题。