文章目录
-
- 一、限流的本质与算法演进
-
- 四种经典算法解析
-
- [1️⃣ 固定窗口计数器法(Fixed Window Counter)](#1️⃣ 固定窗口计数器法(Fixed Window Counter))
- [2️⃣ 滑动窗口法(Sliding Window Log/Counter)](#2️⃣ 滑动窗口法(Sliding Window Log/Counter))
- [3️⃣ 漏桶算法(Leaky Bucket)](#3️⃣ 漏桶算法(Leaky Bucket))
- [4️⃣ 令牌桶算法(Token Bucket)](#4️⃣ 令牌桶算法(Token Bucket))
- 二、分层限流架构
- 三、工程级方案详解
-
- [方案一:Guava 本地令牌桶(单机限流)](#方案一:Guava 本地令牌桶(单机限流))
- [方案二:Redis + Lua Script 分布式滑动窗口限流](#方案二:Redis + Lua Script 分布式滑动窗口限流)
-
- [Lua 脚本示例(`sliding_window.lua`)](#Lua 脚本示例(
sliding_window.lua)) - [Java 接入示例](#Java 接入示例)
- 时序图说明
- [Lua 脚本示例(`sliding_window.lua`)](#Lua 脚本示例(
- [方案三:Alibaba Sentinel 微服务限流](#方案三:Alibaba Sentinel 微服务限流)
- 四、方案对比与选型建议
- 五、实战优化与注意事项
- 六、总结
一、限流的本质与算法演进
在高并发系统中,限流(Rate Limiting) 是应对突发流量、保护下游系统资源的核心手段之一。
限流的目的是在保证系统可用性的前提下,平滑地削峰填谷。
- 核心算法思维导图:
限流核心算法
Fixed Window Counter
简单实现
存在毛刺问题
Sliding Window
平滑控制
Redis ZSet实现
Leaky Bucket
输出速率恒定
平滑突发流量
Token Bucket
定期发放令牌
支持突发流量
四种经典算法解析
1️⃣ 固定窗口计数器法(Fixed Window Counter)
原理:
在固定时间窗口内统计请求总数,当超过阈值后拒绝请求。
优点: 简单易实现
缺点: 临界区间可能出现突发流量 ("毛刺")
2️⃣ 滑动窗口法(Sliding Window Log/Counter)
原理:
将时间等分为若干小窗口,随着时间推进不断滑动删除过期记录。
优点: 精度高、流量分布更均匀
缺点: 统计基于历史记录,略有性能消耗
3️⃣ 漏桶算法(Leaky Bucket)
原理:
请求流如同水流进桶,桶底以固定速率漏水(处理请求),超出部分直接溢出。
特点: 输出速率稳定,强制限速
4️⃣ 令牌桶算法(Token Bucket)
原理:
系统按固定速率生成令牌放入桶中,请求需消耗令牌才能处理。
特点:
支持一定程度的突发流量,互联网高并发限流首选。
二、分层限流架构
限流既有"地面防线"(应用层),也有"网络关卡"(网关层),多层协同构成系统保护伞。
网络入口
网关层限流 Nginx/OpenResty
分布式限流 Redis + Lua
应用层限流 Sentinel/Guava
业务处理与降级
三层典型架构
| 层级 | 技术栈 | 核心用途 |
|---|---|---|
| 网关层 | Nginx / OpenResty / Kong | 拦截恶意流量、防DDoS |
| 分布式中间层 | Redis + Lua / Redisson | 统一全局限流、原子操作 |
| 应用层 | Guava / Sentinel | 微服务保护、错误隔离 |
三、工程级方案详解
方案一:Guava 本地令牌桶(单机限流)
java
import com.google.common.util.concurrent.RateLimiter;
public class LocalLimitService {
// 每秒生成 5 个令牌
private final RateLimiter rateLimiter = RateLimiter.create(5.0);
public void accessApi() {
if (rateLimiter.tryAcquire()) {
System.out.println("请求成功,执行业务逻辑");
} else {
System.out.println("限流中,请稍后再试");
throw new RuntimeException("Too Many Requests");
}
}
}
适用场景:
非分布式系统、小型服务限流。
方案二:Redis + Lua Script 分布式滑动窗口限流
思路:
利用 Redis 的 ZSet 数据结构,将请求时间戳记录为 score,实现可滑动的时间窗口统计。
Lua 脚本示例(sliding_window.lua)
lua
-- KEYS[1]: 限流Key
-- ARGV[1]: 窗口时间 (毫秒)
-- ARGV[2]: 当前时间 (毫秒)
-- ARGV[3]: 阈值(最大请求数)
local key = KEYS[1]
local window_size = tonumber(ARGV[1])
local now = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])
-- 1. 移除过期数据
redis.call('ZREMRANGEBYSCORE', key, 0, now - window_size)
-- 2. 查看当前计数
local current_count = redis.call('ZCARD', key)
-- 3. 判断是否超限
if current_count < limit then
redis.call('ZADD', key, now, now .. "-" .. math.random(10000))
redis.call('PEXPIRE', key, window_size + 1000)
return 1
else
return 0
end
Java 接入示例
java
public boolean allowRequest(String key, int limit, long windowMillis) {
String script = loadLuaScript("sliding_window.lua");
long now = System.currentTimeMillis();
Object result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(key),
windowMillis, now, limit
);
return (Long) result == 1;
}
时序图说明
Redis Server Client Redis Server Client alt [允许] [超限] 请求API 执行Lua限流脚本 返回是否允许(1/0) 业务正常响应 返回 HTTP 429
优点:
- 原子操作(Lua脚本执行不可分割)
- 精确控制时间窗口
- 支持分布式水平扩展
方案三:Alibaba Sentinel 微服务限流
优势:
- 提供完善的控制台
- 支持热点参数限流、链路限流
- 可与 Nacos/Apollo 动态配置联动
java
@SentinelResource(value = "createOrder", blockHandler = "handleBlock")
public String createOrder(String orderId) {
return "订单创建成功: " + orderId;
}
public String handleBlock(String orderId, BlockException ex) {
return "系统繁忙,请稍后再试 (限流)";
}
规则配置:
- QPS 阈值:10
- 流控模式:直接
- 控制效果:快速失败
四、方案对比与选型建议
| 场景 | 推荐方案 | 优势 | 典型技术 |
|---|---|---|---|
| 单机任务或测试环境 | Guava RateLimiter | 简单可靠 | Java |
| Web 集群统一限流 | Redis + Lua | 支持多节点同步限流 | Redis |
| 微服务架构 | Sentinel | 丰富策略&可视化 | Spring Cloud |
五、实战优化与注意事项
限流维度建议
| 限流维度 | 使用场景 | 示例 |
|---|---|---|
| 按 IP | 防爬虫 / DDoS | limit:ip:192.168.0.1 |
| 按用户ID | 防止账号刷接口 | limit:user:1001 |
| 按 API 路径 | 保护热点接口 | limit:api:/order/create |
| 按参数 | 热点商品限流 | limit:goods:sku123 |
限流之后的应对策略
- HTTP 429 返回(Too Many Requests)
- 进入消息队列(MQ)排队处理
- 返回缓存结果或默认值(降级)
否
是
可降级
异步削峰
请求流入
是否限流?
正常处理
返回429
返回缓存/默认响应
进入消息队列排队
多级限流策略(热点Key优化)
分布式限流在极端高并发场景下可能导致 Redis 热点。
✅ 建议方案:本地缓存 + Redis 双层限流。
text
- 本地限流 80% 请求
- Redis 限流校验剩余 20%
六、总结
🔹 限流是高可用系统的"安全阀",能有效削峰、保护服务稳定。
🔹 Redis 限流凭借其高性能、原子操作 特性,是业界最常用的分布式限流手段。
🔹 最佳实践:
网关限流兜底 + Redis 全局限流 + 应用层防御
打造"外防恶流、内稳业务"的坚固防线。