目录
1. 什么是限流
2. 常见限流算法
3. 固定窗口算法
4. 滑动窗口算法
5. 漏桶算法
6. 令牌桶算法
7. 限流算法选择
1. 什么是限流
限流(Rate Limiting)是一种应用程序或系统资源管理的策略,用于控制对某个服务、接口或功能的访问速率。它的主要目的是防止过度的请求或流量超过系统的处理能力,从而保护系统的稳定性、可靠性和安全性。
通过限制访问速率,限流可以防止以下问题的发生:
- 过度使用资源:限流可以防止某个用户或客户端过度使用系统资源,从而保护服务器免受过载的影响。
- 防止垃圾请求:限流可以过滤掉恶意或无效的请求,例如恶意攻击、爬虫或垃圾邮件发送等。
- 维护服务质量:通过限制访问速率,可以确保每个请求都能够得到适当的处理和响应时间,从而提高服务质量和用户体验。
- 控制成本:限流可以帮助控制系统资源的使用,避免因为过多的请求而导致不必要的成本增加。
例如在 12306 中,一个列车可能就几百上千人能购买成功,但可能会有远超过这个量级的用户进行抢票,在真正执行抢票逻辑前,可以通过限流算法进行限制,只让少量用户操作购票流程。
2. 常见限流算法
- 漏桶算法:系统清求先进入漏桶,再从漏桶中逐一取出请求执行,控制漏桶的流量。
- 令牌桶算法:系统请求会得到一个令牌,从令牌桶中取出一个令牌执行,控制令牌桶中令牌的数量。
- 计数器算法:系统请求被计数,通过比较当前请求数与限流阈值来判断是否限流。
- 可以阻塞算法:当系统达到限流阈值时,不再接受新请求,等到限流阈值降下来再接受请求。
- 令牌环算法:与令牌桶算法类似,但是在多个令牌桶之间形成环形结构,以便在不同的请求处理速率之间进行平衡。
- 最小延迟算法:基于预测晦个请求的处理时间,并在处理完请求后进行延迟,以控制清求的速率。
- 滑动窗口(常用):基于一个固定大小的时间窗口,允许在该时间窗口内的请求数不超过设定的阈值。这个时间窗口随着时间的推移不断滑动,以适应不同时间段内的请求流量。
3. 固定窗口算法
固定窗口算法又叫计数器算法,是一种简单方便的限流算法。主要通过一个支持原子操作的计数器来累计 1 秒内的请求次数,当 1 秒内计数达到限流阈值时触发拒绝策略。每过 1 秒,计数器重置为 0 开始重新计数。
固定窗口限流中,也是需要定义时间片段和时间窗口,只不过在计数上有一个区别,那就是当随着时间的推移,到
了下一个时间窗口时,固定窗口限流的计数器的数量会被清零。重新开始计数。
固定窗口限流的主要特点是窗口大小是固定的,不管请求是否均匀分布,每个窗口内的请求数量都是相同的。这可
能导致某些时间段内请求过多,而在其他时间段内则很少,不同窗口之间可能出现流量的不平衡。
缺点:
- 固定时间窗口 1s 限流阈值为100,但是前100ms,已经请求来了99个,那么后续的900ms只能通过一个了,基本上没有应对突发流量的能力。
- 在 00:00:00 这个时间窗口的后 500ms,请求通过了 100 个,在 00:00:01 这个时间窗口的前 500ms 还有100个请求通过,对于服务来说相当于 1 秒内请求量达到了限流阈值的 2 倍。
4. 滑动窗口算法
滑动窗口限流是一种流量控制策略,用于控制在一定时间内允许执行的操作数量或请求频率。它的工作方式类似于一个滑动时间窗口,在窗口内允许的操作数量是固定的,窗口会随着时间的推移不断滑动。
首先需要把时间划分成多个连续的时间片段,每一个片段都有一个固定的时间间隔,如1s、1h等。
然后再定义一个时间窗口,比如10s,随着时间的推移,这个窗口不断的向右移动。为了实现限流的功能,我们通
常需要定义一个计数器,统计时间窗口内的请求数。
当时间窗口移动时,需要把上一个时间片段中的请求数减掉,当有新的请求或操作到达系统时,系统会检查窗口内
的计数是否已满。如果计数未满,请求被允许执行;如果计数已满,请求被拒绝或进入等待队列,或执行其他限流
操作。
滑动窗口限流的主要优点是可以在时间内平滑地控制流量,而不是简单地设置固定的请求数或速率。这使得系统可
以更灵活地应对突发流量或峰值流量,而不会因为固定速率的限制而浪费资源或降低系统性能。
滑动窗口限流可以在分布式系统、AP服务、网络通信等各种应用场景中使用,以确保系统的稳定性和可用性,防
止过多的请求或操作对系统造成负担或崩溃。
缺点:
时间窗口划分的越细,并且按照时间"滑动",这种算法避免了固定窗口计数器出现的上述两个问题。缺点是时间区间的精度越高,算法所需的空间容量就越大。
常见的实现方式主要有基于 edis zset 的方式和循环队列实现。基于 redis zset 可将 Key 为限流标识ID,Value 保持唯一,可以用 UUID 生成,Score 也记为同一时间戳,最好是纳秒级的。使用 redis 提供的 ZADD、EXPIRE、ZCOUNT 和 zremrangebyscore 来实现,并同时注意开启 Pipeline 来尽可能提升性能。实现很简单,但是缺点就是zset的数据结构会越来越大。
5. 漏桶算法
漏桶算法是一种流量控制算法,可以平滑控制流量的进出,原理比较简单:假设我们有一个水桶按固定的速率向下方滴落一滴水,无论有多少请求,请求的速率有多大,都按照固定的速率流出,对应到系统中就是按照固定的速率处理请求。
漏桶算法通过一个固定容量的漏桶来控制请求的处理速率,每个请求被看作是一定数量的水,需要先放到漏桶中。
当漏桶满时,请求将被拒绝或延迟处理,从而保证了系统的稳定
漏桶通过定时器的方式将水以恒定的速率流出,与请求的数量无关,从而平滑控制了请求的处理速率。当请求到来
时,先将请求看作是一定数量的水,需要将这些水放入漏桶中。
总之,漏桶算法通过一个固定容量的漏桶来控制请求的处理速率,可以平滑控制流量的进出,保证系统的稳定性和
安全性。
缺点:
漏桶算法无法处理突发流量,因为他只能按照固定的速度来处理请求,如果某个请求的流量突增,因为漏桶的机制就导致了他还是只能一个一个的按照固定速度进行消费。
6. 令牌桶算法
令牌桶其实和漏桶的原理类似,令牌桶按固定的速率往桶里放入令牌,并且只要能从桶里取出令牌就能通过。
也就是说,不管现在请求量是多还是少,都有一个线程以固定的速率再往桶里放入令牌,而有请求过来的时候,就会去桶里取出令牌,能取到就执行,取不到就拒绝或者阻塞。
令牌桶通过定时器的方式向桶中添加令牌,每秒钟添加一定数量的令牌,从而平滑控制了请求的处理速率。这样如
果突发流量过来了,只要令牌桶中还有足够的令牌,就可以快速的执行,而不是像漏桶一样还要按照固定速率执
行。
令牌桶的好处就是把流量给平滑掉了,在流量不高的时候也会不断的向桶中增加令牌,这样就有足够的令牌可供请
求消费。
7. 限流算法选择
- 固定窗口:实现简单,但是过于粗暴,除非情况紧急,为了能快速止损眼前的问题可以作为临时应急的方案。
- 滑动窗口:限流算法简单易实现,可以应对有少量突增流量场景。
- 漏桶:对于流量绝对均匀有很强的要求,资源的利用率上不是极致,但其宽进严出模式,保护系统的同时还留有部分余量,是一个通用性方案。
- 令牌桶:系统经常有突增流量,并尽可能的压榨服务的性能。