博客 / 詳情

返回

分佈式系統架構5:限流設計模式

分佈式系統架構5:限流設計模式

這是小卷對分佈式系統架構學習的第5篇文章,今天來學習限流器和限流設計模式

1.為什麼要限流?

任何一個系統的運算、存儲、網絡資源都不是無限的,當系統資源不足以支撐外部超過預期的突發流量時,就應該要有取捨,建立面對超額流量自我保護的機制,而這個機制就是微服務中常説的“限流”

2.四種限流設計模式

説到限流,大家直接的想法就是Sentinel,但是Sentinel限流的原理可能很多人沒去深入理解,或者限流到底是怎麼做的?具體如何進行限流,業界內也有一些常見設計模式。

2.1流量計數器模式

流量計數器是一種最簡單的限流方式,通過記錄固定時間窗口內的請求次數來判斷是否達到限流閾值。如果請求次數超過限制值,則拒絕後續請求。

實現方式:

  • 將時間劃分為固定的時間窗口(如 1 秒、1 分鐘)。
  • 每個窗口維護一個計數器,記錄當前時間窗口內的請求次數。
  • 如果計數器值超過限流閾值,直接拒絕請求;否則增加計數器。

固定窗口邊界問題:

  • 在窗口邊界的兩端,可能存在短時間內超量請求的“臨界問題

比如場景設定:一秒內的TPS大於80時,就限流。

存在問題:即使每一秒的統計流量都沒有超過 80 TPS,也不能説明系統沒有遇到過大於 80 TPS 的流量壓力。比如説系統在連續2秒內都收到60TPS的請求,但是請求發生的時間分別在第1秒的後0.5秒,以及第2秒的前0.5秒。這樣系統實際曾在1秒內發生超過80 TPS的請求。

  • 即使連續若干秒統計流量超過閾值,也不能説明流量壓力一定超過系統承受能力

假設 10 秒的時間片段中,前 3 秒的 TPS 平均值到了 100,而後 7 秒的平均值是 30 左右,此時系統是否能夠處理完這些請求而不產生超時失敗?答案是可以的

存在缺陷:造成上面2個問題得原因是流量計數器模式是對時間點進行離散的統計

2.2滑動窗口模式

概念:時間軸上,一個固定大小的窗口隨時間平滑滾動。任何時刻,靜態地通過窗口內觀察到的信息,都等價於一段長度與窗口大小相等的信息。主要是通過記錄多個較小時間窗口(子窗口)的請求次數,實現更精細化的限流控制

假設:準備觀察的時間片段為 10 秒,以 1 秒作為統計精度,那可以得到一個長度為 10 的數組。設定限流閾值是最近 10 秒內收到的請求不超過 500 個,那麼就需要統計10個子數組的請求總數,是否超過閾值。

優點

  • 解決了固定窗口邊界問題

缺點

  • 只適用於否決式限流,超過閾值的流量就必須失敗

2.3漏桶模式

漏桶可以簡單的理解:小學水池應用題,一個水池,每秒以 X 升速度注水,同時又以 Y 升速度出水,問水池啥時候裝滿。

概念:將請求視為流入漏桶的水,漏桶以固定速率“漏水”。當請求流量超過漏桶的處理能力時,多餘的請求會被丟棄或排隊。其核心思想是平滑請求流量

實現方式

  1. 維護一個隊列(或計數器),用來模擬漏桶。
  2. 新請求到來時,將請求放入桶中。
  3. 按固定速率處理桶中的請求。
  4. 如果桶已滿,則拒絕新請求。

缺點:

  • 比較難確定桶的大小和水流出的速度

2.4令牌桶算法

和漏桶一樣是基於緩衝區的限流算法,簡單理解就是去銀行辦事時在排隊機號取號的場景。

概念:通過固定速率向桶中添加令牌,請求到來時需要先消耗令牌才能被處理。如果桶中沒有足夠的令牌,請求會被拒絕。與漏桶算法不同,令牌桶允許一定的突發流量

實現方式
  1. 維護一個桶,桶中存儲令牌。
  2. 按固定速率(比如限流是1秒100次請求,那麼間隔10ms時間放入令牌)向桶中添加令牌,直到桶滿為止。
  3. 請求到來時從桶中取出令牌,如果沒有令牌就馬上失敗或者進入降級邏輯。

實際開發的時候,不需要專門做放令牌到桶裏這件事,只需要在獲取令牌前,比較一下時間戳與當前時間,就能算出需要放入多少令牌,下面是示例代碼:

private long lastTime = System.currentTimeMillis();
private int tokens = 0; // 當前令牌數
private static final int LIMIT = 100; // 桶容量
private static final int REFILL_RATE = 10; // 令牌添加速率(令牌/秒)

public synchronized boolean tryAcquire() {
    long now = System.currentTimeMillis();
    // 添加令牌
    tokens = Math.min(LIMIT, tokens + (int) ((now - lastTime) / 1000) * REFILL_RATE);
    lastTime = now;

    if (tokens > 0) {
        tokens--;
        return true;
    }
    return false;
}

3.分佈式限流

上面介紹的4種限流算法都只適用於單機限流,或者把系統當做整體來限流。實際應用中仍然需要精細的每個服務的限流。

概念:過將限流邏輯分散到多個節點,同時使用一致性算法保證全侷限流的一致性。它結合了本地限流和集中式限流的優點。

實現方式

  • 基於 Redis + Lua 腳本

    • 使用 Redis 腳本實現分佈式限流,在 Redis 中存儲全局的請求計數器
  • 基於一致性算法

    • 使用分佈式一致性算法(如 Raft、Paxos)維護全局流量狀態
  • 分佈式網關

    • 通過 API 網關(如 Kong、Nginx、Spring Cloud Gateway)實現流量的統一調度和限流。

缺點

  • 實現複雜度高,且網絡通信和一致性操作帶來額外延遲。當流量大時,限流本身會降低系統處理能力

總結

今天學習了4種限流設計模式:流量計數器模式、滑動窗口模式、漏桶模式、令牌桶模式,後面2種都是基於緩衝區的限流算法。簡單瞭解了下分佈式限流的概念。限流本身是有代價的,實際開發中需要權衡方案的代價和收益。後續有時間補充Sentinel的限流原理和其中用了哪些設計模式。

user avatar eisuto 頭像 dm2box 頭像 an_653b347d1d3da 頭像 huzilachadedanche 頭像 lingfeng23 頭像 wodingshangniliao 頭像 jellyfishmix 頭像 lianhuatongzina 頭像 mysteryjack 頭像 xiaojiu_625c14980f596 頭像 yaha_5f66c6f02983e 頭像 willliaowh 頭像
16 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.