在系统应对大流量,高并发的访问时,限流算法可以帮助我们控制流量,从而避免系统负载过高而崩溃。接下来将介绍几种常见的限流算法,包括漏桶算法、令牌桶算法、计数器算法、滑动窗口算法和漏斗算法。
漏桶算法
漏桶算法是一种经典的限流算法,它可以用来控制请求速率。漏桶算法的原理是将水放入一个固定容量的桶中,桶底有一个固定大小的洞,水会以固定速率从洞中流出。如果水的流入速度超过了洞口的流出速率,那么多余的水会被丢弃。因此,漏桶算法通常用于限制网络流量、控制数据传输速率等场景,但是无法应对突发的高流量。
利用这个思路,我们可以用这个算法来控制一个系统能接受的访问流量
下面给一个简单的实现
通过这个例子可以看到如何使用漏桶算法来限制流量
class LeakyBucket {
private water: number = 0 // 水位
private lastLeakTime: number = Date.now();
constructor(private readonly capacity: number, private readonly rate: number) {}
/**
* 处理传入的数据包,并返回是否允许通过
*/
processPacket(packetSize: number): boolean {
// 先漏水
const currentTime = Date.now();
const timeElapsed = currentTime - this.lastLeakTime;
const leakedWater = timeElapsed * this.rate / 1000; // 计算漏水量
this.water = Math.max(this.water - leakedWater, 0); // 漏完水后更新桶内水量
this.lastLeakTime = currentTime;
// 加入新的水量
if (packetSize > this.capacity - this.water) {
// 数据包大小超过了桶的剩余容量,丢弃该数据包
return false;
} else {
// 数据包可以通过,加入桶中
this.water += packetSize;
return true;
}
}
}
const bucket = new LeakyBucket(100, 10); // 创建一个容量为100,流出速率为10的漏桶
function sendDataPacket(packetSize: number): void {
if (bucket.processPacket(packetSize)) {
console.log(`发送成功,数据包大小为 ${packetSize} 字节。`);
} else {
console.log(`发送失败,数据包大小为 ${packetSize} 字节,超过了桶的容量。`);
}
}
sendDataPacket(50); // 发送一个大小为50字节的数据包,应该能够通过
sendDataPacket(80); // 发送一个大小为80字节的数据包,应该失败
sendDataPacket(30); // 发送一个大小为30字节的数据包,应该能够通过
sendDataPacket(50); // 发送一个大小为50字节的数据包,应该失败
当然,我们也可以做一下修改,把流量限制改成次数限制,用来控制请求数量,这就像前端常考的节流功能了。
令牌桶算法
令牌桶算法是另一种经典的限流算法,它的原理是将请求放入一个令牌桶中,然后按照一定速率不断地放出令牌。只有在令牌桶中有令牌时,才能够发出请求。令牌桶算法可以控制单位时间内的请求速率,同时可以应对突发流量,因为只要有足够多的令牌,就可以放请求过去。
class TokenBucket {
private tokens: number = 0 // 当前桶内令牌的数量
private lastRefillTime: number = Date.now(); // 上一次加令牌的时间
constructor(private readonly capacity: number, private readonly rate: number) {}
/**
* 处理传入的请求,并返回是否允许通过
*/
processRequest(): boolean {
// 先加令牌
const currentTime = Date.now();
const timeElapsed = currentTime - this.lastRefillTime;
const tokensToAdd = timeElapsed * this.rate / 1000; // 生成令牌数量
this.tokens = Math.min(this.tokens + tokensToAdd, this.capacity); // 加完令牌后更新桶内令牌数量
this.lastRefillTime = currentTime;
// 判断是否允许通过
if (this.tokens < 1) {
// 限流
return false;
} else {
// 通过
this.tokens -= 1;
return true;
}
}
}
const bucket = new TokenBucket(10, 1);
let c = 0
function handleRequest(): void {
c += 1;
if (bucket.processRequest()) {
console.log("通过。",c);
} else {
console.log("限流。",c);
}
}
// 模拟连续请求,每次通过一个
for(let i = 1; i <= 3; i++) {
setTimeout(() => {
handleRequest()
handleRequest()
}, 1000 * i)
}
总结
漏桶算法:
- 常用于限制网络流量、控制数据传输速率等场景
- 类似前端的节流函数,通过恒定速率来控制访问流量
- 无法应对突发流量
令牌桶算法:
- 恒定速率发放令牌
- 可以通过累积令牌来突发流量
关于其他几个算法,且听下回分解
“ 本文正在参加「金石计划」 ”