简单地实现令牌桶限速

实现

  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;
        }
    }
}
相关推荐
董董灿是个攻城狮8 分钟前
传统计算机视觉之均值滤波初探
算法
m0_748251528 分钟前
PDF在线预览实现:如何使用vue-pdf-embed实现前端PDF在线阅读
前端·vue.js·pdf
中生代技术9 分钟前
3.从制定标准到持续监控:7个关键阶段提升App用户体验
大数据·运维·服务器·前端·ux
ゞ 正在缓冲99%…11 分钟前
leecode377.组合总和IV
数据结构·算法·leetcode
m0_7482393314 分钟前
从零开始:如何在.NET Core Web API中完美配置Swagger文档
前端·.netcore
hjxxlsx29 分钟前
备战 C++ 编程区赛 指南
开发语言·数据结构·c++·算法·贪心算法·区赛
wirepuller_king30 分钟前
C语言期末复习题(PTA)
c语言·开发语言·算法
酷酷的崽79836 分钟前
【递归与回溯深度解析:经典题解精讲(中篇)】—— LeetCode
c++·算法·leetcode
pigzhouyb42 分钟前
指针学习-
java·学习·算法
m0_748232921 小时前
【前端】Node.js使用教程
前端·node.js·vim