基本描述
限流分类
- 单机限流
- 分布式限流
限流的指标
- 每秒处理的事务数 (TPS),每秒请求数 (hits per second)
- 使用 hits per second 作为限流指标
限流规则包含三个部分:时间粒度,接口粒度,最大限流值。
选择单机限流还是分布式限流
- 单机限流一般针对服务负载,防止突发流量压垮服务器
- 分布式限流一般针对在业务侧做细粒度限流
限流算法
- 固定时间窗口
- 首先需要选定一个时间起点,之后每次接口请求到来都累加计数器,如果在当前时间窗口内,根据限流规则(比如每秒钟最大允许 100 次接口请求),累加访问次数超过限流值,则限流熔断拒绝接口请求。当进入下一个时间窗口之后,计数器清零重新计数。
- 限流策略过于粗略,无法应对两个时间窗口临界时间内的突发流量。
- 滑动时间窗口
- 滑动时间窗口限流算法可以保证任意时间窗口内接口请求次数都不会超过最大限流值,但是仍然不能防止在细时间粒度上面访问过于集中的问题
- 令牌桶算法(Token Bucket)
- 接口限制 t 秒内最大访问次数为 n,则每隔 t/n 秒会放一个 token 到桶中;桶中最多可以存放 b 个 token
- 令牌桶大小为 b,所以是可以应对突发流量的
- 没有提前预热的令牌桶,如果做否决式限流,会导致误杀很多请求
- 漏桶算法(Leaky Bucket)
- 漏桶算法稍微不同与令牌桶算法的一点是:对于取令牌的频率也有限制,要按照 t/n 固定的速度来取令牌,所以可以看出漏桶算法对流量的整形效果更加好,流量更加平滑,任何突发流量都会被限流。
分布式限流
- 采用redis+lua的方案做分布式限流 (参考业界实现方案即可,需注意原子性)
- 由于使用中心存储计数的方式性能较差,在业务允许的情况下可以考虑将限制的数量分摊到每个服务(服务数通过服务发现接口获取),间接使用单机限流提升性能。
扩展
- Google的Guava包中的RateLimiter
- 令牌桶算法的解决方案,假设1S需要限流5次;也就是1S会往桶里面方5个Token;如果在这1S内桶满了则不再加请求,如果空了则表示达到限制的上线了,会阻塞,直到有数据加入再次处理。
- [ratelimiter4j] (https://github.com/wangzheng0822/ratelimiter4j)
- 分布式限流算法的性能瓶颈主要在中心计数器 Redis,从我们开源的 ratelimiter4j 压测数据来看,在没有做 Redis sharding 的情况下,基于单实例 Redis 的分布式限流算法的性能要远远低于基于内存的单机限流算法,基于我们的压测环境,单机限流算法可以达到 200 万 TPS,而分布式限流算法只能做到 5 万 TPS。所以,在应用分布式限流算法时,一定要考量限流算法的性能是否满足应用场景,如果微服务接口的 TPS 已经超过了限流框架本身的 TPS,则限流功能会成为性能瓶颈影响接口本身的性能。