• 限流大法:令牌桶算法


    记得很多年前就有喜欢在面试的时候问这个问题:如何在高并发、大流量的时候,进行服务限流?
    不同人能给出不同的解决办法。
    无外乎两种处理:

      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还有很多不同实现方法,比如固定时间窗口,滑动时间窗口等,我这个实现是最简单粗暴的。后面找机会再聊一下滑动时间窗口的实现。

    总结

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

  • 相关阅读:
    新加坡国立大学『3D计算机视觉』课程;Python爬虫知识库;基于SKLearn时序预测模块;从零构建AI推理引擎;前沿论文 | ShowMeAI资讯日报
    28.组合数学
    深度学习入门(6) - 3DV 三维视觉
    使用华为eNSP组网试验⑹-组建基于BGP的网络
    8月6日Spring Boot学习笔记
    民安:揭示环评问题与提出解决之道
    【Oralce】导出所有表名、表注释、创建时间、最后修改时间、主键
    Jetson nano安装torch和torchvision
    深度学习系列48:DeepFaker
    缓存与数据库双写一致性问题解决方案
  • 原文地址:https://www.cnblogs.com/freephp/p/17957226