一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶

文章目录

  • 1、计数器算法
  • 2、滑动窗口算法
  • 3、漏桶算法(漏斗算法)
  • 4、令牌桶算法
  • 5、限流算法总结
  • 6、限流组件

:在开发高并发系统时,有三把利器用来保护系统:缓存、降级和限流。

限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等。你要开发一个限流的框架,那么必不可少的就是要选择一种合适的限流算法。限流算法很多,常见的有几类分别是:计数器算法滑动窗口算法漏桶算法令牌桶算法,具体视业务场景,统计的精准度,限流维度而定。下面逐一讲解。

1、计数器算法

计数器算法是限流算法里最简单也是最容易实现的一种算法。主要用来限制一定时间内的总并发数,计数器限流只要一定时间内的总请求数超过设定的阀值则进行限流,是一种简单粗暴的总数量限流,而不是平均速率限流。比如数据库连接池、线程池、秒杀的并发数;

优点:简单粗暴,单机在Java中可用Atomic等原子类或Semaphore,分布式就用Redis incr。

eg:我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么在一开始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个请求的间隔时间还在1分钟之内,那么说明请求数过多,限流;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter
一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第1张图片

缺点:有一个十分致命的问题,那就是临界问题

eg:假设第1分钟中的第59秒的时候,来了100个请求,这个时候是没有超过100的限制。然后第2分钟的第1秒来了100个请求,相隔几毫秒,一下子进来200个请求,明显大于我们的限流阈值100。也就是说在时间窗口的重置节点处突发请求,可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。所以这个算法不够完美是不是?
一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第2张图片

为了解决这个临界区问题,我们引入了滑动窗口算法

2、滑动窗口算法

滑动窗口,又称rolling window。上边提到的计数器算法中,比如限制1分钟内的访问次数,那么这个1分钟就是一个固定的时间窗口,而滑动窗口是将固定窗口再进行细分成多个窗口。

eg:比如将1分钟的固定窗口细分成6个窗口,那么每个窗口的时间就是10秒。如图整个红色的矩形框表示一个时间窗口,窗口是一直滑动的,每过10秒,我们的时间窗口就会往右滑动一格。按照上边我们的假设:假设第1分钟中的第59秒的时候,来了100个请求,落到灰色格子里,而第2分钟的1:00到达的100个请求会落在橘黄色的格子中,而这时我们的时间窗刚好检测到整个1分钟内(红色框),总请求数量一共是200个,超过了限定的100个,所以此时能够检测出来触发了限流。

Spring Cloud 中的熔断框架 Hystrix,以及 Spring Cloud Alibaba 中的Sentinel 都采用滑动窗口来做数据统计。

优点:减少了临界值带来的并发超过阈值的问题。

一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第3张图片

由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

3、漏桶算法(漏斗算法)

漏桶算法相对前面的计数算法更加柔性,它的原理也很简单,它是一种恒定速率的限流算法,不管请求量是多少,服务端的处理效率是恒定的。漏桶限制的是请求的流出速率。漏桶中装的是请求。

优点:是能够以固定的速率去控制流量,稳定性比较好。

可以认为就是我们常用的漏斗注水漏水的过程。漏桶接受以任意速率流入的水,并且以固定速率将水流出。当水超过桶的容量时,会被溢出,也就相当于被丢弃。

漏桶算法原理与消息队列思想有些类似,都是进行削峰填谷,经过漏桶后请求就能匀速平滑的流出。它是一种恒定速率的限流算法,不管请求量是多少,服务端的处理效率是恒定的。基于 MQ 来实现的生产者消费者模型,其实算是一种漏桶限流算法。
一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第4张图片

缺点:就是无法应对突发流量的来袭,以及处理请求会有延迟

eg:假设你的漏桶出口固定了每秒钟只能通过100个请求,如果此时有150个请求,无论你后方的系统能不能抗住这150个请求,通过漏桶算法都会将另外50个请求进行拦截,只能等前面的100个请求结束后才能继续放行剩下的50个请求,无法应对突发流量的来袭。

面对突发请求,服务的处理速度和平常一样,这其实不是我们想要的。我们更想要的是面对突发流量时,在系统平稳运行的同时又能更快的处理请求,而不是一直按照统一的速率来处理。漏桶算法,可能更多的用来保护别人的接口。另外,漏桶中由于请求是暂存在桶中的,所以请求什么时候能被处理,则是有延时的,这并不符合互联网业务低延时的要求。

4、令牌桶算法

令牌桶是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,填满了就丢弃令牌,请求是否被处理要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求。在流量低峰的时候,令牌桶会出现堆积,因此当出现瞬时高峰的时候,有足够多的令牌可以获取,令牌桶允许一定程度突发流量,只要有令牌就可以处理,支持一次拿多个令牌。令牌桶中装的是令牌。
一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第5张图片

网关层面的限流、或者接口调用的限流,都可以使用令牌桶算法,像 Google 的Guava,和 Redisson 的限流,都用到了令牌桶算法。

优点:令牌桶算法是对漏斗算法的一种改进,除了能够起到限流的作用外,还允许一定程度的流量突发。

与漏桶算法相比,有可能导致短时间内的请求数上升(因为拿到令牌后,就可以访问接口,存在一瞬间将所有令牌拿走的情况),但不会有计数算法那样高的峰值(因为令牌数量是匀速增加的)。所以在应对突发流量的时候令牌桶表现的更佳。

一般自己调用自己的接口,接口会有一定的伸缩性,令牌桶算法,主要用来保护自己的服务器接口。

缺点:例如令牌桶,假如系统上线时没有预热,那么可能会出现由于此时桶中还没有令牌,而导致请求被误杀的情况;

5、限流算法总结

上面我们已经了解了每个限流算法的的特点,并且类比了一些应用场景。综上可知,虽然漏桶、令牌桶对比时间窗口类算法对流量的整形效果更好,但是它们也有各自的缺点。

令牌桶、漏桶算法更适合阻塞式限流的场景,即后台任务类的限流。

而基于时间窗口的限流则更适合互联网实施业务限流的场景,即能处理快速处理,不能处理及时响应调用方,避免请求出现过长的等待时间。

6、限流组件

一般而言我们不需要自己实现限流算法来达到限流的目的,不管是接入层限流还是细粒度的接口限流其实都有现成的轮子使用,其实现也是用了上述我们所说的限流算法。

比如Google Guava 提供的限流工具类 RateLimiter,是基于令牌桶实现的,并且扩展了算法,支持预热功能。

阿里开源的限流框架Sentinel 中的匀速排队限流策略,就采用了漏桶算法。

Nginx 中的限流模块 limit_req_zone,采用了漏桶算法,还有 OpenResty 中的 resty.limit.req库等等。

一文搞懂常见限流算法:计数器、滑动窗口、漏桶、令牌桶_第6张图片

你可能感兴趣的:(《数据结构与算法》系列,《Java高并发编程》系列,算法,滑动窗口算法,漏桶算法,令牌桶算法,微服务限流)