简单地实现令牌桶限速

实现

  1. 按一定的速率往令牌桶添加令牌

  2. 只有获取令牌才能处理任务。

  3. 令牌桶满后,新增的令牌丢弃。

  4. 消费令牌后,发现令牌桶没有令牌,及时把消费的令牌还给令牌桶。

优点

滑动窗口:允许在最小窗口的边界,超过限定的速率。例如:按最小窗口为分钟,每分钟最大消费60次,在01分59秒和02分0秒这2秒的时间内,允许消费120次,其他时间不消费。

漏桶算法:当漏桶满以后,只能按一定速率消费。无法过载处理,缺乏效率。

令牌算法:允许过载处理,可以一下子把令牌桶的令牌消费,由于按一定速率添加令牌,令牌消费按照一定速率限速。消费令牌后,发现令牌桶没有令牌,及时补充令牌,防止添加令牌过慢,系统相对空闲的情况。

TypeScript 复制代码
interface Options {
    interval: number;
    max: number;
}

// 简单地实现令牌桶限速
export class RateLimit {
    private _interval = 60000;
    private _max = 100;
    private _buckets = new Map<string, { count: number, resolves: Set<{count: number, resolve: () => void}> }>();
    constructor(options: Options) {
        this.interval = options.interval;
        this.max = options.max;
        setInterval(() => {
            const tokens = this._max / (this._interval / 1000);
            for (const bucket of this._buckets.values()) {
                if (bucket.count < this._max) {
                    bucket.count += tokens;
                }
                for (const resolveInfo of bucket.resolves) {
                    if (bucket.count - resolveInfo.count > 0) {
                        bucket.count -= resolveInfo.count;
                        resolveInfo.resolve();
                        bucket.resolves.delete(resolveInfo);
                    }
                }
            }
        }, 1000);
    }

    get max() {
        return this._max
    }
    set max(val: number) {
        if (val > 0) {
            this._max = Math.floor(val);
        }
    }

    get interval() {
        return this._interval;
    }
    set interval(val: number) {
        if (val >= 1000) {
            this._interval = Math.floor(val);
        }
    }

    limit(key: string, count = 1) {
        return new Promise<void>((resolve) => {
            if (count <= 0) {
                resolve();
                return;
            }
            let bucket = this._buckets.get(key);
            if (!bucket) {
                bucket = { count: 0, resolves: new Set() };
                this._buckets.set(key, bucket);
            }
            if (bucket.count - count > 0) {
                bucket.count -= count;
                resolve();
            } else {
                bucket.resolves.add({count, resolve});
            }
        });
    }

    consume(key: string, count: number) {
        if (count <= 0) {
            return;
        }
        const bucket = this._buckets.get(key);
        if (!bucket) {
            return;
        }
        // 令牌桶为空,补充令牌
        if (bucket.count == 0) {
            bucket.count += count;
        }
    }
}
相关推荐
liuyang-neu1 小时前
力扣中等 33.搜索旋转排序数组
java·数据结构·算法·leetcode
ganjiee00071 小时前
力扣(leetcode)每日一题 2414 最长的字母序连续子字符串的长度
java·算法·leetcode
一生为追梦2 小时前
Linux 内存管理机制概述
前端·chrome
ly-how2 小时前
leetcode练习 格雷编码
数据结构·算法·leetcode
阿W呀2 小时前
MATLAB-最小二乘辨识
人工智能·算法·matlab
liuyang-neu2 小时前
力扣 中等 162.寻找峰值
数据结构·算法·leetcode
scott1985122 小时前
张正友相机标定算法
数码相机·算法
临江浪怀柔ℳ2 小时前
tb的数数问题(牛客小白月赛)
算法
我只钓小鱼2 小时前
蚁群算法+A*算法寻找多目标最优路径
算法
喝旺仔la2 小时前
使用vue创建项目
前端·javascript·vue.js