定义
- 通过限制到达系统的并发请求数量,保证系统能够正常响应部分用户请求,而对于超过限制的流量,则只能通过拒绝服务的方式保证整体系统的可用性。
- 限流策略一般部署在服务的入口层,比如 API 网关中,这样可以对系统整体流量做塑形。而在微服务架构中,可以在 RPC 客户端中引入限流的策略,来保证单个服务不会被过大的流量压垮。
- 可以在多个维度上对到达系统的流量做控制,比如:
- 可以对系统每分钟处理多少请求做出限制;
- 可以针对单个接口设置每分钟请求流量的限制;
- 可以限制单个 IP、用户 ID 或者设备 ID 在一段时间内发送请求的数量;
- 对于服务于多个第三方应用的开放平台来说,每一个第三方应用对于平台方来说都有一个唯一的 appkey 来标识,也可以限制单个 appkey 的访问接口的速率。
限流算法
固定窗口
- 比如,要限制1分钟内系统只承接1万次请求,最暴力方式就是记录这1分钟的访问系统的请求量有多少,如果超过了1万次,则触发限流策略,直接返回失败。
- 缺陷在于,无法限制短时间内集中的流量,
比如当前这1分钟前50秒没流量、最后10秒9000次请求。然后下一分钟前10秒9000次请求。这20秒有18000次请求,但是因为分布在两个时间窗口,没有触发限流策略。
滑动窗口
- 为了解决固定窗口的缺陷,就有了滑动窗口算法。这个算法原理在于将时间窗口划分为多个小窗口,每个小窗口都有单独的请求计数。
- 解决了临界点时间上突发流量无法控制的问题,但是因为要存储每个小的时间窗口内的计数,所以空间复杂度有所增加。
- 缺陷在于依旧无法限制短时间之内的集中流量(比如每1秒限制100次请求,90次集中在0.1秒内,无法让这些请求变得平滑)
漏桶
- 流量暂存在漏桶中,流出速度是固定的。如果漏桶满了,则多余流量会触发限流策略,被拒绝服务。
- 实现时,一般使用消息队列实现,然后由固定数量的处理程序来消费流量。
- 缺陷在于,在面对突发流量的时候,采用的解决方式是缓存在漏桶中, 这样流量的响应时间就会增长。
令牌桶
- 如果需要一秒限制访问次数N,则应该每隔1/N秒,往桶里放入令牌。
- 处理请求前需要从桶中获取令牌,如果桶为空,则需要等待新令牌或者直接拒绝。
- 桶中令牌总数要有限制,达到限制就不需要往桶里再放令牌了,可以避免瞬时流量高峰的问题。
- 一般会使用 Redis 来存储这个令牌的数量。这样的话,每次请求的时候都需要请求一次 Redis 来获取一个令牌,会增加几毫秒的延迟,性能上会有一些损耗。因此,一个折中的思路是: 可以在每次取令牌的时候,不再只获取一个令牌,而是获取一批令牌,这样可以尽量减少请求 Redis 的次数。