限流大法:令牌桶算法

記得很多年前就有喜歡在面試的時候問這個問題:如何在高併發、大流量的時候,進行服務限流?
不同人能給出不同的解決辦法。
無外乎兩種處理:

    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還有很多不同實現方法,比如固定時間窗口,滑動時間窗口等,我這個實現是最簡單粗暴的。後面找機會再聊一下滑動時間窗口的實現。

總結

令牌桶是一個很常見並且好用的算法,對於那些希望在一段時間內只處理有限請求的場景特別使用。畢竟這潑天的富貴,也要一口一口慢慢喫啊!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章