限流大法:令牌桶算法

记得很多年前就有喜欢在面试的时候问这个问题:如何在高并发、大流量的时候,进行服务限流?

不同人能给出不同的解决办法。

无外乎两种处理:

    1. 在客户端限流。
    1. 在服务端限流。

在客户端限流,就是利用产品设计,让单位时间内(可以是1秒,10秒,30秒,1分钟等)只能发出一定请求数量。给用户友好的交互提醒,让他过一会儿再试。

当然如果遇到懂技术的用户,通过一些手段绕过客户端限流限制,那么服务端又会承受这泼天的密集请求。

在服务端限流是一个比较好的选择,更多的控制权放在服务端。一般考虑在2个地方去实现限流。第一个是利用API Gateway,在网关增加请求速率的限制,把大量的请求直接拦在网关处,从而减少服务器在一定时间内能处理的请求数量。

另一个方案是在API服务里面增加限流逻辑,大体的实现思路是:

初始化一个容量固定(比如N)的bucket并装满N个token。每当一个请求过来,就消耗一个token。当bucket没有token了,就无法处理请求了。

而我们在一个时间间隔之后快速refill满bucket,继续等待请求过来消耗token。

下面用一个js代码来展示一个bucket是如何被消耗 token并且自动refill的:

复制代码
class TokenBucket {
  constructor(capacity, refillRate, refillInterval) {
    this.capacity = capacity;         // Maximum tokens in the bucket
    this.tokens = capacity;           // Initial number of tokens
    this.refillRate = refillRate;     // Number of tokens added per interval
    this.refillInterval = refillInterval; // Interval for refilling tokens in milliseconds

    // Start the refill process
    setInterval(() => this.refill(), this.refillInterval);
  }

  // Refill tokens periodically
  refill() {
    this.tokens = Math.min(this.tokens + this.refillRate, this.capacity);
    console.log(`Refilled. Current tokens: ${this.tokens}`);
  }

  // Attempt to consume tokens
  consume(tokensRequired) {
    if (this.tokens >= tokensRequired) {
      this.tokens -= tokensRequired;
      console.log(`Consumed ${tokensRequired} tokens. Remaining: ${this.tokens}`);
      return true;
    } else {
      console.log(`Not enough tokens. Required: ${tokensRequired}, Available: ${this.tokens}`);
      return false;
    }
  }
}

// Example usage
const bucket = new TokenBucket(10, 1, 1000); // Capacity of 10 tokens, refills 1 token every second

// Simulate sending packets
setInterval(() => {
  const packetSize = 3; // Tokens required per packet
  if (bucket.consume(packetSize)) {
    console.log("Packet sent successfully");
  } else {
    console.log("Failed to send packet due to insufficient tokens");
  }
}, 500); // Attempt to send a packet every 0.5 seconds

关于如何实现refill还有很多不同实现方法,比如固定时间窗口,滑动时间窗口等,我这个实现是最简单粗暴的。后面找机会再聊一下滑动时间窗口的实现。

总结

令牌桶是一个很常见并且好用的算法,对于那些希望在一段时间内只处理有限请求的场景特别使用。毕竟这泼天的富贵,也要一口一口慢慢吃啊!

相关推荐
苏打水com2 天前
第十篇:Day28-30 工程化优化与部署——从“能跑”到“好用”(对标职场“项目上线”需求)
前端·css·vue·html·js
RollingPin3 天前
React Native与Flutter的对比
android·flutter·react native·ios·js·移动端·跨平台开发
我命由我123455 天前
微信小程序 - 页面返回并传递数据(使用事件通道、操作页面栈)
开发语言·前端·javascript·微信小程序·小程序·前端框架·js
小曹要微笑5 天前
HTTP与WebSocket协议深度解析
websocket·网络协议·http·js
酒尘&6 天前
JS数组不止Array!索引集合类全面解析
开发语言·前端·javascript·学习·js
我命由我123458 天前
微信小程序 - scroll-view 的一些要点(scroll-view 需要设置滚动方向、scroll-view 需要设置高度)
开发语言·前端·javascript·微信小程序·小程序·前端框架·js
吳所畏惧8 天前
少走弯路:uniapp里将h5链接打包为apk,并设置顶/底部安全区域自动填充显示,阻止webview默认全屏化
android·安全·uni-app·json·html5·webview·js
by__csdn8 天前
ES6新特性全攻略:JavaScript的现代革命
开发语言·前端·javascript·typescript·ecmascript·es6·js
苏打水com8 天前
第六篇:Day16-18 AJAX进阶+接口对接——实现“前后端数据交互”(对标职场“接口开发”核心需求)
css·okhttp·html·js
我命由我123459 天前
VSCode - VSCode 修改文件树缩进
前端·ide·vscode·前端框架·编辑器·html·js