限流算法之令牌桶算法
1.简介
令牌桶算法的原理是系统以一个恒定的速度往桶里放入令牌,请求需要被处理时,需要先从桶里获取一个令牌。当桶里没有令牌可取时,则拒绝服务。令牌桶算法可以用以下几个概念来描述:
- 令牌将按照固定的速率被放入令牌桶中。
- 桶中最多存放一定数量的令牌,当桶满时,新添加的令牌被丢弃。
- 当一个数据包到达,将从桶中删除对应数量的令牌,然后数据包被发送到网络上。
- 如果桶中的令牌不足,则不会删除令牌,且该数据包将被限流。
2.代码实现
package top.houry.limit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TokenBucketRateLimiter {
private final long capacity; // 桶的容量
private final long tokens; // 初始令牌数
private final long refillRate; // 令牌填充速率(每秒添加的令牌数)
private final long refillInterval; // 令牌填充间隔(毫秒)
private long availableTokens; // 当前可用的令牌数
private long lastRefillTimestamp; // 上次填充令牌的时间戳
private final Lock lock = new ReentrantLock();
public TokenBucketRateLimiter(long capacity, long tokens, long refillRate) {
this.capacity = capacity;
this.tokens = tokens;
this.refillRate = refillRate;
this.refillInterval = 1000 / refillRate; // 计算填充间隔
this.availableTokens = tokens;
this.lastRefillTimestamp = System.currentTimeMillis();
}
public boolean tryAcquire() {
lock.lock();
try {
refillTokens();
if (availableTokens >= 1) {
availableTokens--;
return true;
} else {
return false;
}
} finally {
lock.unlock();
}
}
private void refillTokens() {
long now = System.currentTimeMillis();
if (now - lastRefillTimestamp > refillInterval) {
long tokensToAdd = (now - lastRefillTimestamp) / refillInterval * refillRate;
availableTokens = Math.min(capacity, availableTokens + tokensToAdd);
lastRefillTimestamp = now;
}
}
public static void main(String[] args) throws InterruptedException {
TokenBucketRateLimiter rateLimiter = new TokenBucketRateLimiter(100, 100, 100);
for (int i = 0; i < 150; i++) {
boolean result = rateLimiter.tryAcquire();
System.out.println("请求 " + i + ": " + (result ? "通过" : "拒绝"));
// if (i % 10 == 0) { // 模拟请求间隔
// Thread.sleep(50); // 每10个请求暂停50毫秒
// }
}
}
}