在API接口的高并发场景中,限流是防止服务被突发流量击垮的核心手段,令牌桶算法因为能够支持一定程度的流量突发,同时保证长期流量速率稳定,成为很多场景下的首选限流方案。如果不想引入额外的第三方限流依赖,仅使用Java的基础语法和if流程控制,也可以完整实现该算法。

令牌桶算法核心原理
令牌桶算法的核心逻辑可以拆解为两个核心部分,所有流程都可以通过if条件判断来控制:
- 令牌生成:系统会以固定的速率向一个容量固定的桶中放入令牌,当桶满时新生成的令牌会被丢弃
- 请求处理:当有API请求到达时,先从桶中获取一个令牌,如果获取到则处理请求,如果桶中没有令牌则拒绝请求
基于if控制的实现步骤
1. 定义令牌桶核心属性
首先我们需要定义令牌桶的基础属性,包括桶容量、当前令牌数量、上次令牌更新时间、令牌生成速率:
public class TokenBucketLimiter {
// 令牌桶容量
private final int capacity;
// 当前令牌数量
private int currentTokens;
// 上次令牌更新时间,单位毫秒
private long lastRefillTime;
// 令牌生成速率,单位:个/毫秒
private final double refillRate;
public TokenBucketLimiter(int capacity, double refillRate) {
this.capacity = capacity;
this.currentTokens = capacity;
this.lastRefillTime = System.currentTimeMillis();
this.refillRate = refillRate;
}
}
2. 实现令牌补充逻辑
令牌补充的逻辑需要通过if判断当前时间与上次更新时间的差值,计算这段时间内应该生成的令牌数量,同时控制令牌数量不超过桶容量:
private void refillTokens() {
long now = System.currentTimeMillis();
// 计算距离上次更新的时间差
long timeDelta = now - lastRefillTime;
if (timeDelta > 0) {
// 计算这段时间内应该生成的令牌数
int refillCount = (int) (timeDelta * refillRate);
if (refillCount > 0) {
// 补充令牌,不能超过桶容量
currentTokens = Math.min(capacity, currentTokens + refillCount);
lastRefillTime = now;
}
}
}
3. 实现请求限流判断逻辑
当API请求到达时,先补充令牌,再通过if判断当前是否有可用令牌,决定请求是否放行:
public boolean tryAcquire() {
// 先补充令牌
refillTokens();
// 判断是否有可用令牌
if (currentTokens > 0) {
currentTokens--;
return true;
}
return false;
}
完整使用示例
我们可以将上面的逻辑组合成完整的限流器,在API接口中使用:
public class ApiRateLimitDemo {
// 定义限流器:桶容量10个,每100毫秒生成1个令牌,即每秒生成10个令牌
private static final TokenBucketLimiter limiter = new TokenBucketLimiter(10, 0.01);
public static void main(String[] args) {
// 模拟20个并发请求
for (int i = 0; i < 20; i++) {
new Thread(() -> {
String threadName = Thread.currentThread().getName();
if (limiter.tryAcquire()) {
System.out.println(threadName + " 请求通过,处理业务逻辑");
} else {
System.out.println(threadName + " 请求被限流,拒绝处理");
}
}, "请求线程-" + i).start();
}
}
}
注意事项
- 上面的实现是单线程场景下的简单版本,如果是多线程环境,需要对
currentTokens和lastRefillTime的修改做同步控制,比如使用synchronized关键字或者AtomicInteger类 - 令牌生成速率的单位需要根据实际场景调整,上面的示例用的是个/毫秒,也可以改成个/秒,只需要调整
refillRate的计算方式即可 - 如果接口需要支持突发流量,可以适当调大桶容量,桶容量越大,允许的突发请求数量越多
这种实现方式完全基于Java原生语法和if流程控制,没有依赖任何第三方组件,适合对依赖有严格限制的项目使用,同时逻辑清晰,方便后续根据业务需求调整限流规则。