限流是保护系统免受过载的关键技术,令牌桶和漏桶算法是两种主流的实现方案,其选择需结合业务场景、流量特性和系统目标。以下从策略设计到实践落地进行系统分析:
🛡️ 一、限流策略设计原则
设计限流策略时需综合考虑以下核心目标:
- 稳定性保障:通过控制QPS(每秒请求数)或并发线程数,防止系统资源(CPU、内存、连接池等)耗尽。
- 公平性与优先级:支持按用户、IP或API分级限流,确保关键业务或高优先级请求不被阻塞。
- 动态适应性:结合系统负载(如CPU使用率、响应时间)动态调整阈值,避免静态配置的僵化。
- 低延迟与高吞吐:限流逻辑自身需高效,避免成为性能瓶颈,如通过本地预计算减少分布式校验。
🔄 二、令牌桶算法(Token Bucket)
原理
- 系统以恒定速率(
r tokens/s)向桶内投放令牌,桶容量上限为b。 - 请求需获取令牌才可执行,令牌不足时触发限流(拒绝或等待)。
核心优势
- 突发流量处理:桶内积累的令牌允许短时间内突破平均速率(突发量≤
b),适合秒杀、API突发调用等场景。 - 平滑与弹性:长期流量被约束在平均速率
r,同时保留应对突发的弹性。
适用场景
- API网关限流:如Spring Cloud Gateway内置令牌桶,支持按IP或用户分配令牌。
- 实时交互系统:在线游戏、直播弹幕等需容忍瞬时高并发的场景。
- 资源配额管理:云计算中为租户分配CPU时间片或IO带宽。
实现示例(Java)
public class TokenBucket {
private long tokens; // 当前令牌数
private final long capacity; // 桶容量
private final long refillRate; // 令牌生成速率(tokens/s)
private long lastRefillTime; // 上次填充时间
public synchronized boolean tryConsume(int tokensNeeded) {
refill(); // 按时间差补充令牌
if (tokens >= tokensNeeded) {
tokens -= tokensNeeded;
return true;
}
return false;
}
private void refill() {
long now = System.currentTimeMillis();
long elapsed = now - lastRefillTime;
long newTokens = elapsed * refillRate / 1000;
tokens = Math.min(capacity, tokens + newTokens);
lastRefillTime = now;
}
}
⏳ 三、漏桶算法(Leaky Bucket)
原理
- 请求如“水流”进入容量为
c的桶,桶底以固定速率(r requests/s)漏出请求进行处理。 - 桶满时新请求被拒绝或排队。
核心优势
- 严格平滑输出:无论输入流量如何波动,输出速率恒为
r,避免下游系统被突发流量冲击。 - 简单易实现:无需动态计算令牌,仅需队列和定时任务。
适用场景
- 流量整形(Traffic Shaping):音视频流传输需恒定码率(如直播推流)。
- 防DoS攻击:强制恶意流量的速率稳定化,便于识别异常[citation:9]。
- 老旧系统保护:下游服务扩容困难时,漏桶可屏蔽流量波动。
实现示例(Java)
public class LeakyBucket {
private final BlockingQueue<Request> bucket = new ArrayBlockingQueue<>(100); // 桶容量
private final int leakRate; // 漏出速率(requests/s)
public void startLeak() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
if (!bucket.isEmpty()) {
process(bucket.poll()); // 固定速率处理请求
}
}, 0, 1000 / leakRate, TimeUnit.MILLISECONDS);
}
public boolean enqueue(Request request) {
return bucket.offer(request); // 桶满时返回false
}
}
⚖️ 四、令牌桶 vs 漏桶:关键对比与选型建议
| 维度 | 令牌桶 | 漏桶 |
|---|---|---|
| 流量特征 | 允许突发流量(≤桶容量) | 输出绝对平滑,无视输入波动 |
| 延迟控制 | 突发请求低延迟 | 请求可能排队,延迟增加 |
| 实现复杂度 | 需维护令牌生成与时间计算 | 仅需队列+定时任务 |
| 典型场景 | 电商秒杀、API突发调用、实时游戏 | 音视频传输、金融交易系统 |
| 适用目标 | 保护自身系统,容忍瞬时高峰 | 保护下游系统,强制流量平稳 |
选型决策树:
- 是否需要处理突发流量?
- 是 → 令牌桶(如用户活动高峰期API调用)。
- 否 → 漏桶(如支付接口要求每秒处理上限固定)。
- 是否需低延迟响应?
- 是 → 令牌桶(如实时弹幕)。
- 否 → 漏桶(如离线报表生成)。
🛠️ 五、实施建议
- 分布式限流:
- 使用Redis+Lua脚本实现集群级令牌桶(如
RedisRateLimiter)。 - 优化:批量获取令牌(如每次取10个),减少Redis请求次数。
- 使用Redis+Lua脚本实现集群级令牌桶(如
- 动态调参:
- 基于实时监控(如Prometheus)调整
r和b:CPU>80%时降低r,突发期临时提升b。
- 基于实时监控(如Prometheus)调整
- 熔断降级配合:
- 限流后触发降级(如返回缓存数据),避免用户感知为错误。
- 多级限流架构:
- 网关层:全局漏桶控制总入口流量。
- 服务层:本地令牌桶应对单机突发。
💎 总结
- 令牌桶:优先选择应对突发流量场景(如C端高并发业务),强调弹性与低延迟。
- 漏桶:用于严格控速场景(如金融、音视频),确保流量绝对平滑。
实际系统中常组合使用(如网关层漏桶+服务层令牌桶),并辅以动态参数和监控告警,实现自适应流量治理。
1059

被折叠的 条评论
为什么被折叠?



