关于令牌桶和漏桶算法的碎碎念

该文章根据 CC-BY-4.0 协议发表,转载请遵循该协议。
本文地址:https://fenying.net/post/2023/09/25/idea-about-rate-limit-algorithms/

关于漏桶算法和令牌桶算法的一些理解。

昨晚抽空看了一会令牌桶(Token Bucket)和漏桶(Leaky Bucket)两个算法。

想着令牌桶那么简单,每个实例只需要两个变量就够了,伪代码:

 1class TokenBucket {
 2
 3    private _lastRefillTime: number = Date.now();
 4    private _tokens: number;
 5
 6    constructor(
 7        private readonly _capacity: number,
 8        private readonly _refillInterval: number,
 9    ) {
10
11        this._tokens = _capacity;
12    }
13
14    private _tryRefill(): void {
15
16        const now = Date.now();
17
18        const elapsed = now - this._lastRefillTime;
19
20        const refillTokens = Math.floor(elapsed / this._refillInterval);
21
22        if (refillTokens > 0) {
23
24            this._tokens = Math.min(this._capacity, this._tokens + refillTokens);
25            this._lastRefillTime = now;
26        }
27    }
28
29    public consume(): boolean {
30
31        this._tryRefill();
32
33        if (!this._tokens) {
34
35            return false;
36        }
37
38        this._tokens--;
39
40        return true;
41    }
42}

而漏桶却那么复杂,还得弄个队列?

仔细想了下,貌似漏桶只需要关注什么时候漏出去,且漏桶的流出速率是固定的,也就是说——只需要关注当前桶里最后一个元素什么时候会漏出去。 根据这个时间,一可以算出当前桶的大小,也可以算出下一个新元素什么时候漏出去,只需要让当前请求等到那个时候再执行即可。 那每个实例只需要一个变量就够了,比令牌桶还简单……😓

 1async function wait(ms: number): Promise<void> {
 2
 3    return new Promise(resolve => setTimeout(resolve, ms));
 4}
 5
 6class LeakyBucket {
 7
 8    private _nextWaitUntil: number = Date.now();
 9
10    public constructor(
11        private readonly _capacity: number,
12        private readonly _leakInterval: number,
13    ) {}
14
15    public async leak(): Promise<void> {
16
17        const now = Date.now();
18        const elapsed = this._nextWaitUntil - now;
19
20        if (elapsed <= 0) { // 当前桶里的元素已经漏完了,也要等一个周期才能漏出去。
21
22            this._nextWaitUntil = now + this._leakInterval;
23            await wait(this._leakInterval);
24            return;
25        }
26
27        const bucketSize = Math.ceil(elapsed / this._leakInterval);  // 当前桶里的元素个数
28
29        if (bucketSize > this._capacity) {
30
31            // 桶满了,拒绝请求
32            throw new Error('Queue overflow');
33        }
34
35        this._nextWaitUntil = this._nextWaitUntil + this._leakInterval;
36        await wait(this._nextWaitUntil - now);
37    }
38}
comments powered by Disqus