限流的作用
由于对外提供的API接口无法控制调用方的行为,当遇到瞬时流量激增(流量突刺)的情况时,可能会导致接口占用过多的服务器资源,导致其他的请求响应速度降低或超时,又或是服务器承受不住导致宕机。所以限流的作用就是保护服务器,防止流量突刺的情况导致服务崩溃。
限流 (Ratelimiting) 指对应用服务的请求进行限制,例如某一接口的请求限制为每秒一百次, 对超过限制的请求则进行快速失败或丢弃。
限流解决的问题:
- 热点业务带来的流量突刺
- 调用方bug导致的情况
- 恶意攻击
因此,对于公开的接口最好采取限流措施。
限流的算法
固定窗口计数器
概念:
- 将时间划分为多个窗口
- 每个窗口都有计数器
- 如果计数器超过了限制数量,则后续请求都丢弃,直到下个窗口时,将计数器重置
优点:
- 实现简单,原理简单
缺点:
- 策略不好,无法限制两个时间窗口临界时的瞬时流量
假设1秒最多通过5个请求,在[500ms,1000ms]时,来了5个请求,在[1000ms,1500ms]时,来了100个请求,实际上这一秒服务器承受了10个请求,超过的每秒5个请求的限制,不符合预期。
滑动窗口计数器
概念:
- 将时间划分为更多的窗口
- 用一个大窗口维护多个小窗口
- 每结果一个小窗口的时间,抛弃最早的小窗口,增加新的小窗口
- 如果当前大窗口的请求超过了请求限制则拒绝请求
- 滑动窗口计数器是通过将窗口再细分,并且按照时间 " 滑动 ",这种算法避免了固定窗口计数器带来的双倍突发请求
优点:
- 限流相较于固定窗口更加平滑
缺点:
- 滑动窗口只是固定窗口的一种改进,但是治标不治本
- 没有从根本上解决流量突发的问题
- 时间区间的精度越高,算法所需的空间容量就越大。
假设1s限流100个请求,结果100个请求全发生在前10ms内
漏桶
概念:
- 将请求当作水滴,流入一个桶中
- 漏桶以固定速度漏水
- 如果漏桶满了则丢弃多于请求
- 其实就是维护一个固定长度的队列
优点:
- 解决了流量突刺的情况,因为漏桶按照固定的速率漏出请求
缺点:
- 无法应对流量突发问题,当短时间内有大量请求时,即时服务器内没有任何任务,每个请求也得在队列中等待固定速率的时间才能被响应
令牌桶
概念:
- 令牌桶算是漏桶算法的一种改进算法,解决漏桶无法应对突发流量的问题
- 令牌以固定速率生成
- 生成的令牌放入令牌桶中,多余的令牌则丢弃
- 当请求到达时,尝试从令牌桶中获取令牌,取到令牌则可以执行
- 如果令牌桶空了,请求拿不到令牌,则丢弃请求
优点:
- 能够将请求平均分布到时间区间内
- 能够接收服务器承受范围内的突发请求
- 现在比较主流的限流算法
限流的实现
- Golang 标准库限流器
Golang 标准库限流器 time/rate 实现剖析
常见限流算法和go语言time/rate go.uber.org/ratelimit讲解
- uber-go官方库限流
uber-gp/ratelimit
常见限流算法和go语言time/rate go.uber.org/ratelimit讲解