限流场景&限流方案的一些思考
限流场景
-
保护系统资源:限流可用于保护系统资源,防止系统被过度请求而导致资源耗尽或系统崩溃。例如,限制对数据库、缓存、消息队列等关键资源的访问速率,以确保系统的稳定性和可用性。
-
防止恶意攻击:限流可用于防止恶意攻击和恶意请求,如暴力破解、DDoS 攻击等。通过限制来自单个 IP 地址或单个用户的请求速率,可以有效地减轻攻击的影响,并保护系统的安全。
-
流量控制:限流可用于控制系统的流量,防止流量突增而导致系统负载过高。例如,限制某个 API 接口或服务的请求速率,以确保系统能够处理流量峰值,避免系统性能下降或服务不可用。
-
平滑流量:限流可用于平滑流量,防止突发流量对系统造成冲击。通过限制请求速率,可以平滑流量的到达时间,减少系统负载的波动,提高系统的稳定性和可靠性。
-
保护第三方服务:限流可用于保护对第三方服务的调用,防止对第三方服务造成过度压力。例如,限制对外部 API 的请求速率,以确保系统能够遵守第三方服务的使用规范,避免被封禁或受到惩罚。
常用限流算法
-
令牌桶算法:
- 基于令牌桶实现的限流算法。
- 每秒生成一定数量的令牌,请求需要获取令牌才能被处理。
- 令牌桶算法可以应对突发流量。
-
漏桶算法:
- 将访问请求的数据包放入“漏桶”中。
- 以恒定速率处理请求,保证稳定性。
令牌桶算法的理解
系统以恒定速率向桶中放入令牌。如果请求(request)需要处理,必须先从桶中获取一个令牌。当桶中没有令牌时,拒绝请求(request)。
漏桶算法的理解
所有的请求(request)缓冲至桶内,系统以恒定的速率处理请求(request),当请求(request)的量累积至溢出桶外,拒绝请求(request)。
需要更平滑的流量控制,令牌桶算法可能更适合;如果你需要严格的速率控制,漏桶算法可能更合适。
由于令牌桶允许突发流量在阈值范围内激增,因此个人认为它的适用性更为广泛。
限流常用方案
- Guava RateLimiter: Guava 提供的限流类库,简单易用,支持多种限流模式。
- Java 9 java.util.concurrent.RateLimiter: Java 9 提供的限流类库,与 Guava RateLimiter 类似,但性能更高。(要求JDK版本必须9以上)
- Redis: Redis 可以使用 Lua 脚本实现令牌桶限流,支持分布式限流。
- Alibaba的Sentinel组件自带限流功能,基于滑动窗口算法实现,支持多种策略。
值得注意的是:将限流措施写在接口处时,流量已经抵达了系统内,才会进行限流判断。这种做法有时候可能会导致系统过载或不稳定性,尤其是在高流量或突发流量的情况下。
为了更有效地保护系统的稳定性,通常建议在流量的入口处或者在系统的边缘进行限流,例如在外部的反向代理层(比如 Nginx)或者 API 网关(比如 Zuul、Kong 等)上实施限流措施。
+--------------+
| 客户端 |
+--------------+
|
v
+--------------+
| 反向代理层 |
+--------------+
|
v
+--------------+
| 网关层 |
+--------------+
|
+------------- v -------------+
| |
| |
+--------------+ +--------------+
| 服务实例1 | | 服务实例2 |
+--------------+ +--------------+
限流的基准
比如一个获取短信验证码的接口,你往往需要细化限流的基准,不能因为一个恶意用户的超频请求触发限流,导致其他正常用户请求失败。
此处可以参考我的另一篇博客
无论使用何种方式实现限流,都要考虑触发限流后,是否会影响正常用户。