概念
对比基于时间窗口的限流算法,令牌桶和漏桶算法对流量整形效果比时间窗口算法要好很多,但是并不是整形效果越好就越合适,对于没有提前预热的令牌桶,如果做否决式限流,会导致误杀很多请求。上述算法中当 n 比较小时,比如 50,间隔 20ms 才会向桶中放入一个令牌,而接口的访问在 1s 内可能随机性很强,这就会出现:尽管从曲线上看对最大访问频率的限制很有效,流量在细时间粒度上面都很平滑,但是误杀了很多本不应该拒绝的接口请求。
所以令牌桶和漏桶算法比较适合阻塞式限流,比如一些后台 job 类的限流,超过了最大访问频率之后,请求并不会被拒绝,而是会被阻塞到有令牌后再继续执行。对于像微服务接口这种对响应时间比较敏感的限流场景,会比较适合选择基于时间窗口的否决式限流算法,其中滑动时间窗口限流算法空间复杂度较高,内存占用会比较多,所以对比来看,尽管固定时间窗口算法处理临界突发流量的能力较差,但实现简单,而简单带来了好的性能和不容易出错,所以固定时间窗口算法也不失是一个好的微服务接口限流算法。
问题考虑
1.微服务架构中没有接口限流,可能会遇到哪些问题?
2.针对微服务接口限流,如何选择合适的限流算法?
3.如何根据场景和性能要求权衡选择单机还是分布式限流?
为了提高服务的性能和可用性,微服务都会多实例集群部署,所谓单机限流是指:独立的对集群中的每台实例进行接口限流,比如限制每台实例接口访问的频率为最大 1000 次 / 秒,单机限流一般使用单机限流算法;所谓的分布式限流是指:提供服务级的限流,限制对微服务集群的访问频率,比如限制 A 调用方每分钟最多请求 1 万次“用户服务”,分布式限流既可以使用单机限流算法也可以使用分布式限流算法。
单机限流的初衷是防止突发流量压垮服务器,所以比较适合针对并发做限制。分布式限流适合做细粒度限流或者访问配额,不同的调用方对不同的接口执行不同的限流规则,所以比较适合针对 hits per second 限流。从保证系统可用性的角度来说,单机限流更具优势,从防止某调用方过度竞争服务资源来说,分布式限流更加适合。
4.如何根据业务需求灵活的选择不同的限流熔断机制?
5.如何对接口选择合适的限流时间粒度和最大限流值?
6.如何验证微服务接口限流功能的有效性和正确性?
7.如何打造高度容错、高 TPS、低延迟的限流框架?
实现方案
区分场景:
阻塞算法只适合每个请求都必须要处理的场景,实现算法令牌桶算法,漏桶算法。
拒绝算法适合限流,实现算法固定时间窗口,滑动时间窗口。
最佳方案:
拒绝流量。即时间算法。
拒绝流量
拒绝算法,适合限流,而且实现简单。
当然,阻塞算法也可以放弃请求,即拒绝请求。只不过,适合的应用场景有区别。因为漏桶算法是按固定速度来处理请求,而时间算法是按固定时间来处理请求。
即,一个是1ms处理一个请求,一个是1s处理1000个请求。
这两个有什么区别呢?就是我们的需求,应该是1ms可以处理10个请求,而不是1个请求。而如果是采用时间算法,那么1s内的任何1ms,都可以处理10个请求。但是,如果是漏桶算法,那么就只能处理1个请求,其他9个请求处理不了,就只能丢弃请求了。
本质的区别是,两个维度。当然,如果硬要去解决的话,漏桶算法也可以做到1ms处理10个请求,当然归根结底,还是适用的应用场景有一些区别的,不是说一种算法就只能解决一种算法的问题,而解决不了另外一种算法的问题,关键是适合。
阻塞流量
阻塞其实也是限流,本质上是控制流量。
因为阻塞算法只适合每个请求都必须要处理的场景。阻塞,就是必须要消费这些数据。比如,支付系统里的实名认证接口,每一个请求都是必须要处理的。但是你的线程池里的阻塞队列是1000,超过了就只能先阻塞,等队列数据被消费了,再入队列。