精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

分布式限流方案的探索與實踐

開發
本文詳細介紹了幾種限流算法,比較各個算法的優缺點,給出了限流算法選型的一些建議,同時對業務上常用的分布式限流也提出一些解決方案。

隨著微服務的流行,服務之間的依賴性和調用關系變得越來越復雜,服務的穩定性變得尤為重要。業務場景中經常會涉及到瞬時流量沖擊,可能會導致請求響應超時,甚至服務器被壓垮、宕機不可用。出于對系統本身和上下游服務的保護,我們通常會對請求進行限流處理,快速拒絕超出配置上限的請求,保證系統或上下游服務系統的穩定。合理策略能有效應對流量沖擊,確保系統可用性和性能。本文詳細介紹了幾種限流算法,比較各個算法的優缺點,給出了限流算法選型的一些建議,同時對業務上常用的分布式限流也提出一些解決方案。

一、背景

保護高并發服務穩定主要有三把利器:緩存、降級和限流。

  • 緩存:緩存是一種提高數據讀取性能的技術,通過在內存中存儲經常訪問的數據,可以減少對數據庫或者其他存儲系統的訪問,從而提高系統的響應速度。緩存可以應用在多個層次,例如瀏覽器緩存、CDN 緩存、反向代理緩存、應用緩存等。
  • 降級:降級是在系統壓力過大或者部分服務不可用的情況下,暫時關閉一些非核心的服務,以保證核心服務的正常運行。降級可以在多個層次進行,例如頁面降級、功能降級、服務降級等。
  • 限流:限流是一種控制系統處理請求的速率的技術,以防止系統過載。限流可以通過多種算法實現,例如令牌桶算法、漏桶算法等。

這三把"利器"各有其特點,通常會結合使用,以達到最佳的效果。例如,可以通過緩存來減少數據庫的訪問,通過降級來應對系統故障,通過限流來防止系統過載。在設計高并發系統時,需要根據系統的具體需求和特點,合理地使用這些技術。接下來本文會主要介紹一些業界常用的限流方法。

二、限流知識概述

限流是一種對請求或并發數進行限制的關鍵技術手段,旨在保障系統的正常運行。當服務資源有限、處理能力有限時,限流可以對調用服務的上游請求進行限制,以防止自身服務因資源耗盡而停止服務。

在限流中,有兩個重要的概念需要了解:

  • 閾值:指在單位時間內允許的請求量。例如,將 QPS(每秒請求數)限制為500,表示在1秒內最多接受500次請求。通過設置合適的閾值,可以控制系統的負載,避免過多的請求導致系統崩潰或性能下降。
  • 拒絕策略:用于處理超過閾值的請求的策略。常見的拒絕策略包括直接拒絕、排隊等待等。直接拒絕會立即拒絕超過閾值的請求,而排隊等待則將請求放入隊列中,按照一定的規則進行處理。選擇合適的拒絕策略可以平衡系統的穩定性和用戶體驗。

通過合理設置閾值和選擇適當的拒絕策略,限流技術可以幫助系統應對突發的請求量激增、惡意用戶訪問或請求頻率過高的情況,保障系統的穩定性和可用性。限流方案根據限流范圍,可分為 單機限流和分布式限流 ;其中單機限流依據算法,又可分為 固定窗口、滑動窗口、漏桶和令牌桶限流等常見四種 。本文將對上述限流方案進行詳細介紹。

三、限流基本算法

1.固定窗口限流

(1) 算法介紹

固定窗口算法是一種簡單直觀的限流算法,其原理是將時間劃分為固定大小的窗口,在每個窗口內限制請求的數量或速率。具體實現時,可以使用一個計數器來記錄當前窗口內的請求數,并與預設的閾值進行比較。固定窗口算法的原理如下:

  • 將時間劃分固定大小窗口,例如每秒一個窗口。
  • 在每個窗口內,記錄請求的數量。
  • 當有請求到達時,將請求計數加一。
  • 如果請求計數超過了預設的閾值(比如3個請求),拒絕該請求。
  • 窗口結束后,重置請求計數。

(2) 代碼實現

type FixedWindowLimiter struct {
   windowSize  time.Duration // 窗口大小
   maxRequests int           // 最大請求數
   requests    int           // 當前窗口內的請求數
   lastReset   int64         // 上次窗口重置時間(秒級時間戳)
   resetMutex  sync.Mutex    // 重置鎖
}

func NewFixedWindowLimiter(windowSize time.Duration, maxRequests int) *FixedWindowLimiter {
   return &FixedWindowLimiter{
      windowSize:  windowSize,
      maxRequests: maxRequests,
      lastReset:   time.Now().Unix(),
   }
}

func (limiter *FixedWindowLimiter) AllowRequest() bool {
   limiter.resetMutex.Lock()
   defer limiter.resetMutex.Unlock()

   // 檢查是否需要重置窗口
   if time.Now().Unix()-limiter.lastReset >= int64(limiter.windowSize.Seconds()) {
      limiter.requests = 0
      limiter.lastReset = time.Now().Unix()
   }

   // 檢查請求數是否超過閾值
   if limiter.requests >= limiter.maxRequests {
      return false
   }

   limiter.requests++
   return true
}

func main() {
   limiter := NewFixedWindowLimiter(1*time.Second, 3) // 每秒最多允許3個請求
   for i := 0; i < 15; i++ {
      now := time.Now().Format("15:04:05")
      if limiter.AllowRequest() {
         fmt.Println(now + " 請求通過")
      } else {
         fmt.Println(now + " 請求被限流")
      }
      time.Sleep(100 * time.Millisecond)
   }
}

執行結果:

(3) 優缺點

優點:

  • 實現簡單:固定窗口算法的實現相對簡單,易于理解和部署。
  • 穩定性較高:對于突發請求能夠較好地限制和控制,穩定性較高。
  • 易于實現速率控制:固定窗口算法可以很容易地限制請求的速率,例如每秒最多允許多少個請求。

缺點:

  • 請求分布不均勻:固定窗口算法中,窗口內的請求分布可能不均勻,導致某些窗口內的請求數量超過閾值,而其他窗口內的請求較少。
  • 無法應對突發流量:固定窗口算法的窗口大小是固定的,無法靈活地應對突發流量。
  • 請求的不公平性:窗口結束時的請求計數重置可能導致請求的不公平性。例如,在窗口結束前的最后一秒內,請求計數已滿,而在窗口開始時的第一秒內,請求計數為零。

固定窗口算法適用于對請求速率有明確要求且流量相對穩定的場景,但對于突發流量和請求分布不均勻的情況,可能需要考慮其他更靈活的限流算法。

2. 滑動窗口限流

(1) 算法介紹

上文已經說明當遇到時間窗口的臨界突變時,固定窗口算法可能無法靈活地應對流量的變化。例如,在一個時間窗口結束時,如果突然出現大量請求,固定窗口算法可能會導致請求被拒絕,即使在下一個時間窗口內的請求并不多。這種情況下,我們需要一種更靈活的算法來應對突發流量和請求分布不均勻的情況—滑動窗口算法。該算法是對固定窗口算法的一種改進,它通過動態調整窗口的大小來更好地適應流量的變化。與固定窗口算法不同,滑動窗口算法可以在遇到下一個時間窗口之前調整窗口的大小,以便更好地控制請求的速率。算法的原理如下:

  • 窗口大小:確定一個固定的窗口大小,例如1秒。
  • 請求計數:在窗口內,每次有請求到達時,將請求計數加1。
  • 限制條件:如果窗口內的請求計數超過了設定的閾值,即超過了允許的最大請求數,就拒絕該請求。
  • 窗口滑動:隨著時間的推移,窗口會不斷滑動,移除過期的請求計數,以保持窗口內的請求數在限制范圍內。

動態調整:在滑動窗口算法中,我們可以根據實際情況調整窗口的大小。當遇到下一個時間窗口之前,我們可以根據當前的流量情況來調整窗口的大小,以適應流量的變化。

(2) 代碼實現

package main

import (
   "fmt"
   "sync"
   "time"
)

type SlidingWindowLimiter struct {
   windowSize   time.Duration // 窗口大小
   maxRequests  int           // 最大請求數
   requests     []time.Time   // 窗口內的請求時間
   requestsLock sync.Mutex    // 請求鎖
}

func NewSlidingWindowLimiter(windowSize time.Duration, maxRequests int) *SlidingWindowLimiter {
   return &SlidingWindowLimiter{
      windowSize:  windowSize,
      maxRequests: maxRequests,
      requests:    make([]time.Time, 0),
   }
}

func (limiter *SlidingWindowLimiter) AllowRequest() bool {
   limiter.requestsLock.Lock()
   defer limiter.requestsLock.Unlock()

   // 移除過期的請求
   currentTime := time.Now()
   for len(limiter.requests) > 0 && currentTime.Sub(limiter.requests[0]) > limiter.windowSize {
      limiter.requests = limiter.requests[1:]
   }

   // 檢查請求數是否超過閾值
   if len(limiter.requests) >= limiter.maxRequests {
      return false
   }

   limiter.requests = append(limiter.requests, currentTime)
   return true
}

func main() {
   limiter := NewSlidingWindowLimiter(500*time.Millisecond, 2) // 每秒最多允許4個請求
   for i := 0; i < 15; i++ {
      now := time.Now().Format("15:04:05")
      if limiter.AllowRequest() {
         fmt.Println(now + " 請求通過")
      } else {
         fmt.Println(now + " 請求被限流")
      }
      time.Sleep(100 * time.Millisecond)
   }
}

執行結果:

(3) 優缺點

優點:

  • 靈活性:滑動窗口算法可以根據實際情況動態調整窗口的大小,以適應流量的變化。這種靈活性使得算法能夠更好地應對突發流量和請求分布不均勻的情況。
  • 實時性:由于滑動窗口算法在每個時間窗口結束時都會進行窗口滑動,它能夠更及時地響應流量的變化,提供更實時的限流效果。
  • 精度:相比于固定窗口算法,滑動窗口算法的顆粒度更小,可以提供更精確的限流控制。

缺點:

  • 內存消耗:滑動窗口算法需要維護一個窗口內的請求時間列表,隨著時間的推移,列表的長度會增長。這可能會導致較大的內存消耗,特別是在窗口大小較大或請求頻率較高的情況下。
  • 算法復雜性:相比于簡單的固定窗口算法,滑動窗口算法的實現較為復雜。它需要處理窗口滑動、請求計數和過期請求的移除等邏輯,可能需要更多的代碼和計算開銷。

從代碼的角度可以發現,滑動窗口算法實際上是顆粒度更小的固定窗口算法,它可以在一定程度上提高限流的精度和實時性,并不能從根本上解決請求分布不均勻的問題。算法受限于窗口的大小和時間間隔,特別是在極端情況下,如突發流量過大或請求分布極不均勻的情況下,仍然可能導致限流不準確。因此,在實際應用中,要采用更復雜的算法或策略來進一步優化限流效果。

3. 漏桶限流

(1) 算法介紹

盡管滑動窗口算法可以提供一定的限流效果,但它仍然受限于窗口的大小和時間間隔。在某些情況下,突發流量可能會導致窗口內的請求數超過限制。為了更好地平滑請求的流量,漏桶限流算法可以作為滑動窗口算法的改進。算法的原理很簡單:它維護一個固定容量的漏桶,請求以不定的速率流入漏桶,而漏桶以固定的速率流出。如果請求到達時,漏桶已滿,則會觸發拒絕策略。可以從生產者-消費者模式去理解這個算法,請求充當生產者,每個請求就像一滴水,當請求到達時,它被放入一個隊列(漏桶)中。而漏桶則充當消費者,以固定的速率從隊列中消費請求,就像從桶底的孔洞中不斷漏出水滴。消費的速率等于限流閾值,例如每秒處理2個請求,即500毫秒消費一個請求。漏桶的容量就像隊列的容量,當請求堆積超過指定容量時,會觸發拒絕策略,即新到達的請求將被丟棄或延遲處理。算法的實現如下:

  • 漏桶容量:確定一個固定的漏桶容量,表示漏桶可以存儲的最大請求數。
  • 漏桶速率:確定一個固定的漏桶速率,表示漏桶每秒可以處理的請求數。
  • 請求處理:當請求到達時,生產者將請求放入漏桶中。
  • 漏桶流出:漏桶以固定的速率從漏桶中消費請求,并處理這些請求。如果漏桶中有請求,則處理一個請求;如果漏桶為空,則不處理請求。
  • 請求丟棄或延遲:如果漏桶已滿,即漏桶中的請求數達到了容量上限,新到達的請求將被丟棄或延遲處理。

(2) 代碼實現

package main

import (
   "fmt"
   "time"
)

type LeakyBucket struct {
   rate       float64 // 漏桶速率,單位請求數/秒
   capacity   int     // 漏桶容量,最多可存儲請求數
   water      int     // 當前水量,表示當前漏桶中的請求數
   lastLeakMs int64   // 上次漏水的時間戳,單位秒
}

func NewLeakyBucket(rate float64, capacity int) *LeakyBucket {
   return &LeakyBucket{
      rate:       rate,
      capacity:   capacity,
      water:      0,
      lastLeakMs: time.Now().Unix(),
   }
}

func (lb *LeakyBucket) Allow() bool {
   now := time.Now().Unix()
   elapsed := now - lb.lastLeakMs

   // 漏水,根據時間間隔計算漏掉的水量
   leakAmount := int(float64(elapsed) / 1000 * lb.rate)
   if leakAmount > 0 {
      if leakAmount > lb.water {
         lb.water = 0
      } else {
         lb.water -= leakAmount
      }
   }

   // 判斷當前水量是否超過容量
   if lb.water > lb.capacity {
      lb.water-- // 如果超過容量,減去剛剛增加的水量
      return false
   }

   // 增加水量
   lb.water++

   lb.lastLeakMs = now
   return true
}

func main() {
   // 創建一個漏桶,速率為每秒處理3個請求,容量為4個請求
   leakyBucket := NewLeakyBucket(3, 4)

   // 模擬請求
   for i := 1; i <= 15; i++ {
      now := time.Now().Format("15:04:05")
      if leakyBucket.Allow() {
         fmt.Printf(now+"  第 %d 個請求通過\n", i)
      } else {
         fmt.Printf(now+"  第 %d 個請求被限流\n", i)
      }
      time.Sleep(200 * time.Millisecond) // 模擬請求間隔
   }
}

執行結果:

(3) 優缺點

優點:

  • 平滑流量:漏桶算法可以平滑突發流量,使得輸出流量變得更加平穩,避免了流量的突然增大對系統的沖擊。
  • 簡單易實現:漏桶算法的原理簡單,實現起來也相對容易。
  • 有效防止過載:通過控制流出的流量,漏桶算法可以有效地防止系統過載。

缺點:

  • 對突發流量的處理不夠靈活:雖然漏桶算法可以平滑突發流量,但是在某些情況下,我們可能希望能夠快速處理突發流量。在這種情況下,漏桶算法可能就不夠靈活了。
  • 無法動態調整流量:漏桶算法的流出速率是固定的,無法根據系統的實際情況動態調整。
  • 可能會導致流量浪費:如果輸入流量小于漏桶的流出速率,那么漏桶的流出速率就會被浪費。
  • 如果輸入流量持續大于漏桶的流出速率,那么漏桶會一直滿,新的請求會被丟棄,可能會導致服務質量下降。

4. 令牌桶限流

(1) 算法介紹

令牌桶算法是實現限流是一種常見的思路,用于限制請求的速率。它可以確保系統在高負載情況下仍能提供穩定的服務,并防止突發流量對系統造成過載。最為常用的 Google 的 Java 開發工具包 Guava 中的限流工具類 RateLimiter 就是令牌桶的一個實現。令牌桶算法基于一個令牌桶的概念,其中令牌以固定的速率產生,并放入桶中。每個令牌代表一個請求的許可。當請求到達時,需要從令牌桶中獲取一個令牌才能通過。如果令牌桶中沒有足夠的令牌,則請求被限制或丟棄。 令牌桶算法的實現步驟如下:

  • 初始化一個令牌桶,包括桶的容量和令牌產生的速率。
  • 持續以固定速率產生令牌,并放入令牌桶中,直到桶滿為止。
  • 當請求到達時,嘗試從令牌桶中獲取一個令牌。
  • 如果令牌桶中有足夠的令牌,則請求通過,并從令牌桶中移除一個令牌。
  • 如果令牌桶中沒有足夠的令牌,則請求被限制或丟棄。

(2) 代碼實現

package main

import (
   "fmt"
   "sync"
   "time"
)

// TokenBucket 表示一個令牌桶。
type TokenBucket struct {
   rate       float64    // 令牌添加到桶中的速率。
   capacity   float64    // 桶的最大容量。
   tokens     float64    // 當前桶中的令牌數量。
   lastUpdate time.Time  // 上次更新令牌數量的時間。
   mu         sync.Mutex // 互斥鎖,確保線程安全。
}

// NewTokenBucket 創建一個新的令牌桶,給定令牌添加速率和桶的容量。
func NewTokenBucket(rate float64, capacity float64) *TokenBucket {
   return &TokenBucket{
      rate:       rate,
      capacity:   capacity,
      tokens:     capacity, // 初始時,桶是滿的。
      lastUpdate: time.Now(),
   }
}

// Allow 檢查是否可以從桶中移除一個令牌。如果可以,它移除一個令牌并返回 true。
// 如果不可以,它返回 false。
func (tb *TokenBucket) Allow() bool {
   tb.mu.Lock()
   defer tb.mu.Unlock()

   // 根據經過的時間計算要添加的令牌數量。
   now := time.Now()
   elapsed := now.Sub(tb.lastUpdate).Seconds() 
   tokensToAdd := elapsed * tb.rate            

   tb.tokens += tokensToAdd
   if tb.tokens > tb.capacity {
      tb.tokens = tb.capacity // 確保令牌數量不超過桶的容量。
   }

   if tb.tokens >= 1.0 {
      tb.tokens--
      tb.lastUpdate = now
      return true
   }

   return false
}

func main() {
   tokenBucket := NewTokenBucket(2.0, 3.0)

   for i := 1; i <= 10; i++ {
      now := time.Now().Format("15:04:05")
      if tokenBucket.Allow() {
         fmt.Printf(now+"  第 %d 個請求通過\n", i)
      } else { // 如果不能移除一個令牌,請求被拒絕。
         fmt.Printf(now+"  第 %d 個請求被限流\n", i)
      }
      time.Sleep(200 * time.Millisecond)
   }
}

執行結果:

(3) 優缺點

優點:

  • 平滑流量:令牌桶算法可以平滑突發流量,使得突發流量在一段時間內均勻地分布,避免了流量的突然高峰對系統的沖擊。
  • 靈活性:令牌桶算法可以通過調整令牌生成速率和桶的大小來靈活地控制流量。
  • 允許突發流量:由于令牌桶可以積累一定數量的令牌,因此在流量突然增大時,如果桶中有足夠的令牌,可以應對這種突發流量。

缺點:

  • 實現復雜:相比于其他一些限流算法(如漏桶算法),令牌桶算法的實現稍微復雜一些,需要維護令牌的生成和消耗。
  • 需要精確的時間控制:令牌桶算法需要根據時間來生成令牌,因此需要有精確的時間控制。如果系統的時間控制不精確,可能會影響限流的效果。
  • 可能會有資源浪費:如果系統的流量持續低于令牌生成的速率,那么桶中的令牌可能會一直積累,造成資源的浪費。

5. 四種基本算法的對比

四、分布式限流

單機限流指針對單一服務器的情況,通過限制單臺服務器在單位時間內處理的請求數量,防止服務器過載。常見的限流算法上文已介紹,其優點在于實現簡單,效率高,效果明顯。隨著微服務架構的普及,系統的服務通常會部署在多臺服務器上,此時就需要分布式限流來保證整個系統的穩定性。接下本文會介紹幾種常見的分布式限流技術方案:

1. 基于中心化的限流方案

(1) 方案原理

通過一個中心化的限流器來控制所有服務器的請求。實現方式:

  • 選擇一個中心化的組件,例如—Redis。
  • 定義限流規則,例如:可以設置每秒鐘允許的最大請求數(QPS),并將這個值存儲在Redis中。
  • 對于每個請求,服務器需要先向Redis請求令牌。
  • 如果獲取到令牌,說明請求可以被處理;如果沒有獲取到令牌,說明請求被限流,可以返回一個錯誤信息或者稍后重試。

(2) 代碼實現

package main

import (
   "context"
   "fmt"
   "go.uber.org/atomic"
   "sync"

   "git.code.oa.com/pcg-csd/trpc-ext/redis"
)

type RedisClient interface {
   Do(ctx context.Context, cmd string, args ...interface{}) (interface{}, error)
}

// Client 數據庫
type Client struct {
   client RedisClient // redis 操作
   script string      // lua腳本
}

// NewBucketClient 創建redis令牌桶
func NewBucketClient(redis RedisClient) *Client {
   helper := redis
   return &Client{
      client: helper,
      script: `
         -- 令牌桶限流腳本
         -- KEYS[1]: 桶的名稱
         -- ARGV[1]: 桶的容量
         -- ARGV[2]: 令牌產生速率
         
         local bucket = KEYS[1]
         local capacity = tonumber(ARGV[1])
         local tokenRate = tonumber(ARGV[2])
         
         local redisTime = redis.call('TIME')
         local now = tonumber(redisTime[1])
         
         local tokens, lastRefill = unpack(redis.call('hmget', bucket, 'tokens', 'lastRefill'))
         tokens = tonumber(tokens)
         lastRefill = tonumber(lastRefill)
         
         if not tokens or not lastRefill then
            tokens = capacity
            lastRefill = now
         else
            local intervalsSinceLast = (now - lastRefill) * tokenRate
            tokens = math.min(capacity, tokens + intervalsSinceLast)
         end
         
         if tokens < 1 then
            return 0
         else
            redis.call('hmset', bucket, 'tokens', tokens - 1, 'lastRefill', now)
            return 1
         end
      `,
   }
}

// 獲取令牌,獲取成功則立即返回true,否則返回false
func (c *Client) isAllowed(ctx context.Context, key string, capacity int64, tokenRate int64) (bool, error) {
   result, err := redis.Int(c.client.Do(ctx, "eval", c.script, 1, key, capacity, tokenRate))
   if err != nil {
      fmt.Println("Redis 執行錯誤:", err)
      return false, err
   }
   return result == 1, nil
}

// 調用檢測
func main() {
   c := NewBucketClient(redis.GetPoolByName("redis://127.0.0.1:6379"))
   gw := sync.WaitGroup{}
   gw.Add(120)
   count := atomic.Int64{}
   for i := 0; i < 120; i++ {
      go func(i int) {
         defer gw.Done()
         status, err := c.isAllowed(context.Background(), "test", 100, 10)
         if status {
            count.Add(1)
         }
         fmt.Printf("go %d status:%v error: %v\n", i, status, err)
      }(i)
   }
   gw.Wait()
   fmt.Printf("allow %d\n\n", count.Load())
}

執行結果:

(3) 存在的問題

  • 性能瓶頸:由于所有的請求都需要經過Redis,因此Redis可能成為整個系統的性能瓶頸。為了解決這個問題,可以考慮使用Redis集群來提高性能,或者使用更高性能的硬件。
  • 單點故障:如果Redis出現故障,整個系統的限流功能將受到影響。為了解決這個問題,可以考慮使用Redis的主從復制或者哨兵模式來實現高可用。
  • 網絡帶寬:Redis是一個基于網絡通信的內存數據庫,因此網絡帶寬是其性能的一個關鍵因素。如果網絡帶寬有限,可能會導致請求的傳輸速度變慢,從而影響Redis的性能。

redis官方性能測試圖

2. 基于負載均衡的分布式限流方案

(1) 方案原理

可以看到中心化限流方案的存在較高的單點故障風險,且帶寬瓶頸比較嚴重。在這個基礎上本文結合本地緩存單機限流和負載均衡設計了一個新的分布式限流方案。具體方案如下:

  • 使用負載均衡器或分布式服務發現(北極星即可做到),將請求均勻地分發到每個機器上。這確保了每個機器都能處理一部分請求。
  • 在每個機器上維護本機的限流狀態,實現本地緩存單機限流的邏輯。使用令牌桶限流算法,在每個機器上獨立地進行限流控制。每秒鐘處理的請求數、令牌桶的令牌數量等。根據本地限流狀態,對到達的請求進行限流判斷。
  • 準備相應的動態調整方案,可以根據每個機器的實際負載情況,動態地調整限流參數。例如,如果一個機器的CPU或內存使用率過高,你可以降低這個機器的限流閾值,減少這個機器的請求處理量。反之,如果一個機器的資源使用率較低,提高這個機器的限流閾值,增加這個機器的請求處理量。

(2) 存在的問題

  • 本地緩存:這個方案對本地緩存的要求較高,需要自己根據業務邏輯抉擇本地緩存的淘汰策略和緩存容量限制等風險點。
  • 限流精度:本地緩存單機限流的精度受限于每個服務實例的資源和配置。這可能導致限流策略無法精確地適應整個系統的流量變化,無法靈活地調整限流規則。
  • 請求負載均衡器的單點故障。
  • 動態擴縮容的適應性:當系統需要動態擴展或縮容時,該方案可能需要額外的配置和和調整,以確保新加入或移除的服務實例能夠正確地參與限流和請求均衡。

3. 基于分布式協調服務的限流

(1) 方案原理

使用ZooKeeper或者etcd等分布式協調服務來實現限流。每臺服務器都會向分布式協調服務申請令牌,只有獲取到令牌的請求才能被處理。基本方案:

  • 初始化令牌桶:在ZooKeeper中創建一個節點,節點的數據代表令牌的數量。初始時,將數據設置為令牌桶的容量。
  • 申請令牌:當一個請求到達時,服務器首先向ZooKeeper申請一個令牌。這可以通過獲取節點的分布式鎖,然后將節點的數據減1實現。如果操作成功,說明申請到了令牌,請求可以被處理;如果操作失敗,說明令牌已經用完,請求需要被拒絕或者等待。
  • 釋放令牌:當一個請求處理完畢時,服務器需要向ZooKeeper釋放一個令牌。這可以通過獲取節點的分布式鎖,然后將節點的數據加1實現。
  • 補充令牌:可以設置一個定時任務,定期向ZooKeeper中的令牌桶補充令牌。補充的頻率和數量可以根據系統的負載情況動態調整。

(2) 存在的問題

這個方案的優點是可以實現精確的全局限流,并且可以避免單點故障。但是,這個方案的缺點是實現復雜,且對ZooKeeper的性能有較高的要求。如果ZooKeeper無法處理大量的令牌申請和釋放操作,可能會成為系統的瓶頸。

五、總結

總之,沒有最好的方案,只有合適的方案。在選擇合適的限流方案時,我們需要考慮多種因素,包括系統的需求、現有的技術棧、系統的負載情況以及底層系統的性能等。理解每種方案的工作原理和特性,以便在實際應用中做出最佳的選擇。

一個好的限流設計必須要考慮到業務的特性和需求,同時具備以下六點:

  • 多級限流:除了主備復制的限流服務,可以考慮實現多級限流策略。例如,可以在應用層、服務層和數據層都設置限流,這樣可以更好地防止系統過載。
  • 動態閾值調整:我們可以根據系統的實時負載情況動態調整限流策略。例如,當系統負載較低時,我們可以放寬限流策略;當系統負載較高時,我們可以收緊限流策略。
  • 靈活維度:限流策略應該能夠根據不同的業務場景進行調整。除了接口,設備,ip,賬戶id等維度外,我們還可以考慮更細粒度的限流。例如,我們可以根據用戶的行為模式進行限流,這樣可以更好地防止惡意用戶的攻擊。
  • 解耦性:限流應該作為一個基礎服務,與具體的業務邏輯分離。這樣,當業務邏輯發生變化時,不需要修改限流服務的代碼,只需要調整限流策略即可。
  • 容錯性:限流服務應該具有高可用性,但是如果出現問題,業務應該有備選方案(熔斷、降級)。這可能包括使用備用的限流服務,或者根據業務的敏感性決定是否放行請求。
  • 監控和報警:對限流策略應進行實時監控,并設置報警機制。當限流策略觸發時,可立即收到報警,以便我們可以及時地處理問題。

限流是保證系統穩定和高效運行的重要手段,但它并不是唯一的解決方案。我們還需要考慮其他的系統設計和優化手段,例如負載均衡、緩存、異步處理等(面對爆量,擴容永遠是最好的方式,除了貴!)。這些手段相互配合,才能構建出一個既能應對高并發請求,又能保證服務質量的系統。

責任編輯:趙寧寧 來源: 騰訊技術工程
相關推薦

2022-09-07 08:18:26

分布式灰度方案分支號

2023-12-29 08:18:31

Session分布式系統微服務

2022-07-18 10:29:33

數據分布式系統

2024-03-26 12:08:53

分布式事務存儲

2025-08-27 09:11:48

2024-01-05 07:28:50

分布式事務框架

2009-02-06 09:38:38

memcached分布式緩存系統ASP.NET

2024-11-19 15:55:49

2024-01-10 08:02:03

分布式技術令牌,

2022-03-01 16:26:09

鏈路監控日志監控分布式系統

2022-01-12 12:46:32

Go限流保障

2021-10-30 19:30:23

分布式Celery隊列

2023-12-08 07:31:19

服務網格協同分布式

2023-02-28 07:01:11

分布式緩存平臺

2024-09-27 09:19:30

2022-03-21 19:44:30

CitusPostgreSQ執行器

2025-09-02 07:16:37

2018-06-11 11:12:09

秒殺限流分布式

2013-03-22 14:44:52

大規模分布式系統飛天開放平臺

2024-07-08 07:30:47

點贊
收藏

51CTO技術棧公眾號

日本久久久久久久久| 在线电影一区二区三区| 国产伦精品一区二区三区视频免费 | 91超碰在线电影| 懂色av蜜臀av粉嫩av永久| 成人免费观看49www在线观看| 亚洲欧洲精品一区二区精品久久久 | 亚洲人成电影网站色mp4| 亚洲一区二区免费在线| 国产一级免费观看| 亚洲人亚洲人色久| 欧美日韩mp4| 久久久久久久久网| 人妻无码中文字幕| 久久久久久一区二区| 色妞色视频一区二区三区四区| 九九热免费在线观看| 99视频免费在线观看| 不卡在线观看av| 国产91对白在线播放| 在线看片中文字幕| 4438全国亚洲精品观看视频| 天天色天天操综合| 中文字幕一区二区三区最新| 亚洲卡一卡二卡三| 日韩中文字幕区一区有砖一区| 国产亚洲精品久久| 美女被爆操网站| 免费观看成人性生生活片 | 91传媒视频在线播放| 椎名由奈jux491在线播放| 国产免费黄色网址| 久久不射中文字幕| 久久偷看各类女兵18女厕嘘嘘| 性久久久久久久久久久| 日韩欧美三区| 色综合久久综合中文综合网| www.-级毛片线天内射视视| 麻豆导航在线观看| 成人久久视频在线观看| 国产精品一区久久久| 成人在线免费看视频| 综合五月婷婷| 色婷婷综合成人| 色婷婷在线影院| 超碰成人免费| 91精品国产欧美日韩| 成人黄色一区二区| h片精品在线观看| 国产精品久久久久久久久快鸭| 麻豆传媒一区| 亚洲欧美国产高清va在线播放| 日本午夜一区二区| 26uuu久久噜噜噜噜| 国产精品丝袜一区二区| 成人av国产| 亚洲欧美日韩国产成人| 日韩免费高清一区二区| 久久国产精品美女| 欧美酷刑日本凌虐凌虐| 免费看污污网站| 日本а中文在线天堂| 亚洲一区二区精品视频| 亚洲一区二区精品在线| 邻家有女韩剧在线观看国语| 国产福利91精品一区二区三区| 国产日韩欧美视频在线| 午夜精品三级久久久有码| 伊人久久成人| 久久久久久久91| 久草视频中文在线| 亚洲先锋影音| 自拍偷拍亚洲一区| 欧美一级片黄色| 男人的天堂久久| 亚洲国产成人精品久久| 国产xxxx视频| 久久porn| 国产丝袜一区二区| 影音先锋人妻啪啪av资源网站| 国产精品久av福利在线观看| 日韩一区二区三区三四区视频在线观看| 在线免费观看av的网站| 久久女人天堂| 91麻豆精品国产91久久久使用方法 | 2019中文字幕全在线观看| 欧美亚韩一区二区三区| 性欧美长视频| 国产精品美女主播| 91肉色超薄丝袜脚交一区二区| 久久99精品国产| 成人免费视频一区| 欧美日韩国产第一页| 九九精品在线观看视频| 亚洲第一精品影视| 欧美怡春院一区二区三区| 亚洲av中文无码乱人伦在线视色| 老司机免费视频久久| 国产精品青草久久久久福利99| 中文av免费观看| 国产精品99精品久久免费| 国产精品一区二区三区不卡| 日本一二三区在线视频| 欧美韩日一区二区三区四区| 99精品一区二区三区的区别| 四虎影视成人| 色综合天天综合狠狠| 欧美亚洲日本在线观看| 精品国产乱码久久久久久樱花| 欧美二区三区91| www.555国产精品免费| 国产99精品一区| 久久久国产一区| 国产成人无码精品久在线观看| 日韩国产欧美视频| 91在线视频一区| 亚洲 欧美 精品| 国产精品超碰97尤物18| 和岳每晚弄的高潮嗷嗷叫视频| 黑人巨大亚洲一区二区久| 正在播放亚洲一区| 中文字幕一区二区久久人妻网站 | 好男人www在线视频| 国产日韩欧美高清| 国产一二三区在线播放| gogo亚洲高清大胆美女人体| 欧美成人三级电影在线| 韩国一级黄色录像| 全部av―极品视觉盛宴亚洲| 久久精品国产一区二区三区日韩| 超碰个人在线| 欧美日韩一级片网站| 可以直接看的无码av| 亚洲激情另类| 97视频中文字幕| 动漫一区在线| 欧美久久久久中文字幕| 正在播放国产对白害羞| 日韩黄色小视频| 欧美日韩精品免费看| 麻豆理论在线观看| 亚洲国产精品yw在线观看| 国产在线成人精品午夜| 国产jizzjizz一区二区| 欧美a级黄色大片| 成人免费91| 另类色图亚洲色图| 国产sm主人调教女m视频| 自拍偷自拍亚洲精品播放| 午夜视频在线网站| 国产精品国内免费一区二区三区| 国产精品免费网站| 秋霞午夜理伦电影在线观看| 欧美三级资源在线| 激情高潮到大叫狂喷水| 久久国产福利国产秒拍| 亚洲一区二区三区免费看| 看片一区二区| 久久久极品av| 精品人妻av一区二区三区| 亚洲综合成人网| xxxwww国产| 美女精品在线| 亚洲国产一区二区三区在线播| 日韩一区二区三区在线免费观看| 中文字幕一区日韩电影| 91成品人影院| 亚洲黄色免费电影| 国产黑丝一区二区| 午夜在线a亚洲v天堂网2018| 清纯唯美一区二区三区| 色噜噜成人av在线| 欧美激情奇米色| 亚洲av片一区二区三区| 91精品办公室少妇高潮对白| а天堂中文在线资源| 粉嫩av一区二区三区在线播放 | 亚洲在线视频| 欧美一区二区三区成人久久片| 91综合国产| 欧美噜噜久久久xxx| 少妇高潮一区二区三区69| 日本高清视频一区二区| 中国毛片直接看| 99久久国产综合色|国产精品| 国产免费视频传媒| 欧美a级在线| 久久久神马电影| 日韩免费在线电影| 96精品视频在线| www在线免费观看| 欧美va亚洲va| 国产一级片av| 亚洲综合成人网| jizz中文字幕| 丁香啪啪综合成人亚洲小说 | 18岁成人毛片| 91色婷婷久久久久合中文| 99sesese| 国产精品婷婷| 波多野结衣激情| 一区二区美女| caoporn国产精品免费公开| 这里有精品可以观看| 久久的精品视频| 欧美xxx.com| 日韩欧美国产1| 伊人网综合在线| 一区二区三区 在线观看视频| 亚洲乱码国产乱码精品精大量| 国产一区在线观看视频| 那种视频在线观看| 国精品一区二区三区| 亚洲国产精品久久久久婷婷老年 | 国产综合色在线视频区| 亚洲欧洲日产国码无码久久99 | 久久久久久久网| 91精品人妻一区二区三区蜜桃2 | 日韩免费在线视频| 青春草在线免费视频| 最近2019年好看中文字幕视频| 天堂av手机版| 精品剧情在线观看| a在线观看视频| 欧美视频一区在线观看| 久久国产黄色片| 亚洲国产成人porn| 欧美人与禽zozzo禽性配| 国产精品美日韩| 韩国三级hd中文字幕| 91网上在线视频| 日韩精品视频一区二区| 成人一区二区视频| 香蕉网在线视频| 国产一区在线看| 在线视频日韩欧美| 激情综合一区二区三区| 在线观看国产一级片| 另类小说综合欧美亚洲| 狠狠操精品视频| 日欧美一区二区| 黄色国产精品视频| 久久xxxx精品视频| 日本一极黄色片| 久久综合九色| 无码无遮挡又大又爽又黄的视频| 国产欧美日本| 国产免费黄色av| 欧美中文日韩| 农村妇女精品一二区| 国产欧美午夜| 国产免费毛卡片| 亚洲欧美成人| www一区二区www免费| 亚洲综合国产| 情侣黄网站免费看| 日韩不卡一二三区| 欧美男女交配视频| 久久爱另类一区二区小说| 天天看片天天操| 国产毛片精品视频| 一二三区视频在线观看| 99视频精品在线| 88久久精品无码一区二区毛片| 国产亚洲一二三区| 日韩黄色中文字幕| 亚洲精选免费视频| 国产无遮挡又黄又爽| 欧美性猛交xxxx免费看| 国内av在线播放| 69堂成人精品免费视频| 亚洲精品成人电影| 亚洲欧美日韩国产中文专区| av大片在线播放| 欧美另类极品videosbestfree| 国产黄色大片在线观看| 日韩免费不卡av| 亚洲人成777| 国产精品视频免费观看| 久草成人资源| 路边理发店露脸熟妇泻火| 亚洲欧洲日本mm| wwwwxxxx日韩| 国产精品77777| 中文字幕日韩三级片| 国产精品丝袜91| 午夜偷拍福利视频| 91福利在线免费观看| www男人的天堂| 夜夜嗨av色一区二区不卡| 二区三区在线观看| 日本道色综合久久影院| 亚洲国产aⅴ精品一区二区三区| 成人区精品一区二区| 国产精品入口久久| 日日噜噜夜夜狠狠久久丁香五月| 国产毛片一区| 污污视频在线免费| 91蝌蚪porny成人天涯| 永久免费看片直接| 日韩欧美国产视频| 国产福利资源在线| 亚洲一级片在线看| av免费不卡| 91精品视频大全| 亚洲婷婷影院| 日韩美女爱爱视频| 麻豆国产欧美一区二区三区| 亚洲激情 欧美| 伊人婷婷欧美激情| 中文字幕在线观看国产| 精品亚洲一区二区| 18网站在线观看| 国产欧美日韩中文字幕| 亚洲国产网址| 免费视频爱爱太爽了| 九一久久久久久| 国产又大又粗又爽的毛片| 午夜精品福利一区二区三区av | 久久成人18免费观看| 国产真人做爰视频免费| 天天综合色天天综合| 亚洲第九十九页| 欧美成人免费va影院高清| 国产精品毛片久久久久久久久久99999999| 国产精品一码二码三码在线| 一本一道久久a久久精品蜜桃| 日本美女高潮视频| 国产偷国产偷精品高清尤物| 国产精品500部| 亚洲激情免费观看| 丁香高清在线观看完整电影视频| 亚洲精品女av网站| 99久久精品国产亚洲精品| 中文字幕在线观看第三页| 久久亚洲综合色| 看片网址国产福利av中文字幕| 欧美xxxxxxxxx| 久久99亚洲网美利坚合众国| 不卡视频一区二区| 欧美高清不卡| 黄页网站在线看| 亚洲黄一区二区三区| www久久久com| 久久久久久久久久久免费 | 日韩美女主播在线视频一区二区三区 | 91大神在线观看线路一区| 免费试看一区| 久久最新视频| 人人爽人人爽人人片| 在线视频观看一区| 2019中文字幕在线视频| 国产精品偷伦一区二区| 99热国内精品永久免费观看| 亚洲免费av一区| 一区二区三区在线视频免费| 亚洲AV午夜精品| 国模gogo一区二区大胆私拍| 欧美一区二区三区久久| 国产又黄又猛视频| 国产精品免费看片| 国产精品自偷自拍| 欧美极品少妇xxxxⅹ裸体艺术| a看欧美黄色女同性恋| 国产男女在线观看| 久久久不卡网国产精品二区| 最新中文字幕在线观看视频| 色999日韩欧美国产| 精品国产鲁一鲁****| 久久久久久久久久网| 久久久久亚洲综合| 国产又粗又长视频| 久久久久久美女| 欧美美女在线| 午夜影院免费版| 午夜精品aaa| av中文在线| 亚洲一区二区三区久久| 亚洲人体偷拍| 黄色av免费播放| 日韩女优毛片在线| 国产不卡网站| www.黄色网址.com| 91在线视频在线| 一本色道久久综合熟妇| 欧美高清视频一区二区| 神马电影久久| 国产成人精品一区二区三区在线观看 | 亚洲激情在线观看| 亚洲午夜国产成人| 人体内射精一区二区三区| 中文子幕无线码一区tr| 成人免费公开视频| 国产精品手机播放| 亚洲理伦在线| 国产三级国产精品国产国在线观看| 亚洲电影第1页| 成人噜噜噜噜| 成人性视频欧美一区二区三区| 亚洲精品国产无天堂网2021|