限流的算法

一、介绍

限流,也称流量控制。是指系统在面临高并发,或者大流量请求的情况下,对于服务提供方的一种保护手段。通过限流功能,我们可以通过控制QPS的方式,以避免被瞬时的流量高峰冲垮,从而保障系统的高可用性。

二、限流算法

2.1 固定窗口限流算法

首先维护一个计数器,将单位时间段当做一个窗口,计数器记录这个窗口接收请求的次数。

1、当次数少于限流阀值,就允许访问,并且计数器+1

2、当次数大于限流阀值,就拒绝访问。

3、当前的时间窗口过去之后,计数器清零。

假设单位时间是1秒,限流阀值为3。在单位时间1秒内,每来一个请求,计数器就加1,如果计数器累加的次数超过限流阀值3,后续的请求全部拒绝。等到1s结束后,计数器清0,重新开始计数。如下图:

但是,这种算法有一个很明显的临界问题:假设限流阀值为5个请求,单位时间窗口是1s,如果我们在单位时间内的前0.8-1s和1-1.2s,分别并发5个请求。虽然都没有超过阀值,但是如果算0.8-1.2s,则并发数高达10,已经超过单位时间1s不超过5阀值的定义啦。

2.2 滑动窗口限流算法

滑动窗口限流解决固定窗口临界值的问题。它将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并且根据时间滑动删除过期的小周期。

假设单位时间还是1s,滑动窗口算法把它划分为5个小周期,也就是滑动窗口(单位时间)被划分为5个小格子。每格表示0.2s。每过0.2s,时间窗口就会往右滑动一格。然后呢,每个小周期,都有自己独立的计数器,如果请求是0.83s到达的,0.8~1.0s对应的计数器就会加1。

我们来看下滑动窗口是如何解决临界问题的?

假设我们1s内的限流阀值还是5个请求,0.8~1.0s内(比如0.9s的时候)来了5个请求,落在黄色格子里。时间过了1.0s这个点之后,又来5个请求,落在紫色格子里。如果是固定窗口算法,是不会被限流的,但是滑动窗口的话,每过一个小周期,它会右移一个小格。过了1.0s这个点后,会右移一小格,当前的单位时间段是0.2~1.2s,这个区域的请求已经超过限定的5了,已触发限流啦,实际上,紫色格子的请求都被拒绝啦。

TIPS: 当滑动窗口的格子周期划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

滑动窗口算法虽然解决了固定窗口的临界问题,但是一旦到达限流后,请求都会直接暴力被拒绝。酱紫我们会损失一部分请求,这其实对于产品来说,并不太友好。

问题: 滑动窗口算法能否精准地控制任意给定时间窗口 T 内的访问量不大于 N?

将 1 分钟分成 6 个 10 秒大小的子窗口的例子,假设请求的速率现在是 20 次/秒,从 0:05 时刻开始进入,那么在 0:05~0:10 时间段内会放进 100 个请求,同时接下来的请求都会被限流,直到 1:00 时刻窗口滑动,在 1:00~1:05 时刻继续放进 100 个请求。如果把 0:05~1:05 看作是 1 分钟的时间窗口,那么这个窗口内实际的请求量是 200,超出了给定的阈值 100。

2.3 漏桶算法

漏桶算法面对限流,就更加的柔性,不存在直接的粗暴拒绝。

它的原理很简单,可以认为就是注水漏水的过程。往漏桶中以任意速率流入水,以固定的速率流出水。当水超过桶的容量时,会被溢出,也就是被丢弃。因为桶容量是不变的,保证了整体的速率。

1、流入的水滴,可以看作是访问系统的请求,这个流入速率是不确定的。

2、桶的容量一般表示系统所能处理的请求数。

3、如果桶的容量满了,就达到限流的阀值,就会丢弃水滴(拒绝请求)

4、流出的水滴,是恒定过滤的,对应服务按照固定的速率处理请求。

在正常流量的时候,系统按照固定的速率处理请求,是我们想要的。但是面对突发流量的时候,漏桶算法还是循规蹈矩地处理请求,这就不是我们想看到的啦。流量变突发时,我们肯定希望系统尽量快点处理请求,提升用户体验嘛。

2.4 令牌桶算法

面对突发流量的时候,我们可以使用令牌桶算法限流。

令牌桶算法原理:

●有一个令牌管理员,根据限流大小,定速往令牌桶里放令牌。

●如果令牌数量满了,超过令牌桶容量的限制,那就丢弃。

●系统在接受到一个用户请求时,都会先去令牌桶要一个令牌。如果拿到令牌,那么就处理这个请求的业务逻辑;

●如果拿不到令牌,就直接拒绝这个请求。

如果令牌发放的策略正确,这个系统即不会被拖垮,也能提高机器的利用率。Guava的RateLimiter限流组件,就是基于令牌桶算法实现的。

2.5 滑动日志

滑动日志算法,单位时间段内,利用记录下来的每个用户的请求时间,及其请求数。当新的一个请求进来后,先把该请求与当前请求日志信息进行对比,如果是新用户,就立即响应处理,否则再根据其请求数是否在窗口期超出预设的请求上限,决定是否处理该请求。

滑动日志能够避免突发流量,实现较为精准的限流;同样更加灵活,能够支持更加复杂的限流策略,如多级限流,每分钟不超过100次,每小时不超过300次,每天不超过1000次,我们只需要保存最近24小时所有的请求日志即可实现。

灵活并不是没有代价的,带来的缺点就是占用存储空间要高于其他限流算法。

优点:避免了窗口算法在窗口边界可能出现的两倍流量问题(比如用户重复提交请求),即避免了用户的惊群效应。 缺点:保存大量日志,且由于完成请求前要比对用户请求数,故在分布式系统下操作难度大?

2.6 总结

固定窗口算法实现简单,性能高,但是会有临界问题。

为了解决临界突发流量,可以将窗口划分为多个更细粒度的单元,每次窗口向右移动一个单元,于是便有了滑动窗口算法。滑动窗口当流量到达阈值时会瞬间掐断流量,所以导致流量不够平滑。

想要达到限流的目的,又不会掐断流量,使得流量更加平滑?可以考虑漏桶算法!需要注意的是,漏桶算法通常配置一个FIFO的队列使用以达到允许限流的作用。由于速率固定,即使在某个时刻下游处理能力过剩,也不能得到很好的利用,这是漏桶算法的一个短板。

限流和瞬时流量其实并不矛盾,在大多数场景中,短时间突发流量系统是完全可以接受的。令牌桶算法就是不二之选了,令牌桶以固定的速率v产生令牌放入一个固定容量为n的桶中,当请求到达时尝试从桶中获取令牌。当桶满时,允许最大瞬时流量为n;当桶中没有剩余流量时则限流速率最低,为令牌生成的速率v。

如何实现更加灵活的多级限流呢?滑动日志限流算法(更精确)!这里的日志则是请求的时间戳,通过计算制定时间段内请求总数来实现灵活的限流。当然,由于需要存储时间戳信息,其占用的存储空间要比其他限流算法要大得多。

令牌桶和漏桶对比:

令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。

一个系统限制 60 秒内的最大访问量是 60 次,换算速率是 1 次/秒,如果在一段时间内没有访问量,那么对漏桶而言此刻是空的。现在,一瞬间涌入 60 个请求,那么流量整形后,漏桶会以每秒 1 个请求的速度,花上 1 分钟将 60 个请求漏给下游。换成令牌桶的话,则是从令牌桶中一次性取走 60 个令牌,一下子塞给下游。

令牌桶:

漏桶:

相关推荐
元Y亨H13 小时前
Nacos - 服务注册
java·微服务
Hi_kenyon13 小时前
VUE3套用组件库快速开发(以Element Plus为例)二
开发语言·前端·javascript·vue.js
曲莫终13 小时前
Java VarHandle全面详解:从入门到精通
java·开发语言
一心赚狗粮的宇叔13 小时前
中级软件开发工程师2025年度总结
java·大数据·oracle·c#
奋进的芋圆14 小时前
DataSyncManager 详解与 Spring Boot 迁移指南
java·spring boot·后端
ghie909014 小时前
基于MATLAB GUI的伏安法测电阻实现方案
开发语言·matlab·电阻
Gao_xu_sheng14 小时前
Inno Setup(专业安装/更新 EXE)
开发语言
计算机程序设计小李同学14 小时前
个人数据管理系统
java·vue.js·spring boot·后端·web安全