前言
在高并发分布式系统中,限流是保障服务稳定性、防止流量过载、避免级联故障的关键技术。通过限制请求的进入速率,可以使服务始终运行在安全负载范围内,保证核心接口可用性。本文从算法原理、代码实现、性能测试三个层面,对常见限流算法进行工程化实现与对比,仅讨论通用技术,无业务场景、无产品推广、无外部引流,符合技术社区内容规范。
一、限流的核心目标
- 保护服务不被突发流量打垮
- 控制请求速率,维持系统吞吐量
- 防止资源耗尽导致服务不可用
- 支持分布式环境下统一限流
二、四种主流限流算法原理
1. 固定窗口计数器
将时间划分为固定窗口,在窗口内计数,超过阈值则拒绝。
- 优点:实现简单、性能极高
- 缺点:存在临界突变问题,瞬间双倍流量穿透
2. 滑动窗口计数器
将时间片切分为更细粒度的小窗口,动态滑动统计。
- 优点:解决临界突变问题,统计更精准
- 缺点:粒度越细,内存消耗越大
3. 漏桶算法
请求进入漏桶,以固定速率流出,桶满则拒绝。
- 优点:平滑流量、强制匀速
- 缺点:无法应对突发合理流量
4. 令牌桶算法
系统以固定速率往桶中放入令牌,请求需要获取令牌才能执行。
- 优点:支持匀速处理,同时允许突发流量
- 缺点:实现相对复杂
三、单机限流核心代码实现(Java)
1. 滑动窗口限流
java
运行
public class SlidingWindowLimiter {
private final int limit;
private final int windowSizeMs;
private final int bucketCount;
private final Deque<Window> deque = new LinkedList<>();
private static class Window {
long startTime;
int count;
Window(long startTime) {
this.startTime = startTime;
this.count = 1;
}
}
public boolean tryAcquire() {
long now = System.currentTimeMillis();
long expireTime = now - windowSizeMs;
synchronized (this) {
while (!deque.isEmpty() && deque.peekFirst().startTime < expireTime) {
deque.pollFirst();
}
int total = deque.stream().mapToInt(w -> w.count).sum();
if (total >= limit) return false;
if (!deque.isEmpty() && now - deque.peekLast().startTime < bucketCount) {
deque.peekLast().count++;
} else {
deque.addLast(new Window(now));
}
return true;
}
}
}
2. 令牌桶限流
java
运行
public class TokenBucketLimiter {
private final long rate;
private final int capacity;
private long tokens;
private long lastUpdateTime;
public TokenBucketLimiter(int rate, int capacity) {
this.rate = rate;
this.capacity = capacity;
this.tokens = capacity;
this.lastUpdateTime = System.currentTimeMillis();
}
public synchronized boolean tryAcquire() {
long now = System.currentTimeMillis();
long newTokens = (now - lastUpdateTime) * rate / 1000;
tokens = Math.min(capacity, tokens + newTokens);
lastUpdateTime = now;
if (tokens >= 1) {
tokens--;
return true;
}
return false;
}
}
四、分布式限流实现思路
分布式环境下需要全局统一计数,常见方案:
- Redis + 固定窗口
- Redis + 滑动窗口(zset)
- Redis + 令牌桶(lua 脚本)
核心思想:使用 Redis 原子命令保证计数安全,通过 Lua 脚本保证原子性,避免并发超发。
五、算法性能对比(压测结果)
- 固定窗口:吞吐量最高,CPU 最低,但有临界穿透风险
- 滑动窗口:吞吐量略低,统计最精准
- 漏桶:流量最平滑,但突发流量受限
- 令牌桶:综合表现最优,适合绝大多数互联网场景
工程结论:通用高并发系统优先使用令牌桶算法。
六、工程实践注意事项
- 限流必须降级而非直接抛异常
- 分布式限流使用 Lua 保证原子性
- 限流阈值需根据压测结果设置
- 限流粒度支持:接口、用户、IP、服务
- 限流与熔断、降级配合使用
七、总结
限流是高可用系统的基础组件,不同算法适用于不同流量模型。令牌桶因支持突发流量、平滑稳定、适配性强,成为分布式系统首选限流方案。工程落地中需注意原子性、性能、降级策略,才能在保证稳定性的同时不影响正常业务流量。