简单地实现令牌桶限速

实现

  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;
        }
    }
}
相关推荐
To_OC4 小时前
写了三遍 Todo List,我终于搞懂了 React 父子组件到底怎么通信
前端·javascript·react.js
勇往直前plus5 小时前
Vue3(篇一) 核心概念——响应式模板语法与组件基础
前端·javascript·vue.js
咩咩啃树皮5 小时前
第43篇:Vue3计算属性(computed)完全精讲——缓存机制、依赖计算、业务最优解
前端·vue.js·缓存
徐小夕7 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
可编程芯片开发8 小时前
UPFC统一潮流控制器的simulink建模与仿真
算法
kyriewen8 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz8 小时前
Rust &‘static str浅析
java·前端·javascript·rust
小小仙子8 小时前
矢量网络分析仪如何测试S参数的?
人工智能·算法·机器学习
IT_陈寒9 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端