简单地实现令牌桶限速

实现

  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;
        }
    }
}
相关推荐
小远yyds11 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
AI街潜水的八角33 分钟前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
白榆maple1 小时前
(蓝桥杯C/C++)——基础算法(下)
算法
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
此生只爱蛋2 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
咕咕吖2 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展