流量控制:高并發系統中我們如何操縱流量?
限流是一種保護系統免受過載的策略,通過限制系統處理請求的速率來確保系統穩定運行。常見的限流策略包括控制并發連接數、基于時間窗口的限流算法(如令牌桶算法)、動態調整限流策略、服務降級和故障熔斷、以及優先級隊列等方法。在微服務架構中,可以在服務入口處或關鍵組件處部署限流策略,同時在RPC客戶端中引入限流策略,以保護系統的穩定性和可靠性。
其實,無論在實際工作生活中還是在之前學習過的知識中,你都可能對限流策略有過應用,我給你舉幾個例子。
限流策略就像是景區限制每天售賣門票數量或者地鐵限制進站人數一樣,通過控制一段時間內的流量,來避免系統或者場所因為擁擠而影響服務質量或者安全。這種策略可以確保系統或者場所在高峰時段不會被過多的用戶或者游客擠爆,保證了服務的穩定性和可靠性。
TCP協議中的滑動窗口就像接收方的一個緩沖區,用來控制發送方發送數據的速度。如果接收方處理速度慢,滑動窗口就變小,限制發送方發送數據的量,避免數據堆積和網絡擁塞。
在TCP協議中,接收方回復發送方的ACK消息中會攜帶滑動窗口的大小。這個滑動窗口大小告訴發送方,接收方目前的處理能力和緩沖區的剩余空間。發送方根據這個窗口大小來決定發送數據的速率。如果接收方處理了部分緩沖區的數據,滑動窗口就會增大,發送方可以加快發送數據的速率。反之,如果接收方的緩沖區快滿了,滑動窗口就會減小,發送方會減慢發送數據的速率,以避免數據丟失或網絡擁塞。
圖片
你應該知道的限流算法
固定窗口與滑動窗口的算法
我們知道,限流的目的是限制一段時間內發向系統的總體請求量,比如,限制一分鐘之內系統只能承接 1 萬次請求,那么最暴力的一種方式就是記錄這一分鐘之內訪問系統的請求量有多少,如果超過了 1 萬次的限制,那么就觸發限流的策略返回請求失敗的錯誤。如果這一分鐘的請求量沒有達到限制,那么在下一分鐘到來的時候先重置請求量的計數,再統計這一分鐘的請求量是否超過限制。
圖片
粗粒度控制: 固定窗口算法在限流時只考慮了一個固定的時間窗口,例如每秒或每分鐘,這種粗粒度的控制可能會導致在時間窗口開始的瞬間發生突發的流量,而在窗口結束時沒有流量的情況,無法靈活應對。
突發請求問題: 固定窗口算法無法很好地處理突發性的請求,在窗口期開始時可能會集中出現大量請求,導致瞬間達到限流閾值,而在接下來的時間段內無法再接受新的請求,造成了資源的浪費和服務的不公平性。
漏桶算法與令牌筒算法
漏桶算法的原理很簡單,它就像在流量產生端和接收端之間增加一個漏桶,流量會進入和暫存到漏桶里面,而漏桶的出口處會按照一個固定的速率將流量漏出到接收端(也就是服務接口)。
令牌桶算法是一種流量控制算法,其基本原理如下:
圖片
定義一個令牌桶,用來存放令牌。
每隔固定時間間隔(1/N秒),向令牌桶中放入一個令牌,直到達到令牌桶的最大容量。
處理請求時,需要從令牌桶中獲取一個令牌,如果令牌桶中沒有令牌,則拒絕服務或者等待直到有令牌可用。
如果令牌桶中的令牌數量超過了最大容量,新的令牌將不再被放入令牌桶中。
通過令牌桶算法,可以限制請求的處理速率,避免系統突發流量造成的問題。由于令牌桶中令牌的數量有限,因此可以有效控制系統的總體請求量。
圖片
在分布式環境下使用令牌桶算法進行限流時,確實需要解決多臺機器之間共享令牌數量的問題。通常可以借助分布式緩存或者分布式鎖來實現。
對于使用Redis來存儲令牌數量的情況,每次請求都需要向Redis請求令牌,可能會增加一定的延遲。為了減少請求Redis的次數,可以采取一些優化策略,比如在每次請求時一次性獲取一批令牌,而不是逐個獲取。這樣可以減少與Redis的交互次數,提高性能。
另外,還可以通過合理設置令牌桶的容量和填充速率,以及調整令牌獲取的頻率,來平衡系統的穩定性和性能損耗。需要根據具體場景和性能需求進行合理的設計和調優。
總結:
限流是一種常見的服務保護策略,你可以在整體服務、單個服務、單個接口、單個 IP 或者單個用戶等多個維度進行流量的控制;
基于時間窗口維度的算法有固定窗口算法和滑動窗口算法,兩者雖然能一定程度上實現限流的目的,但是都無法讓流量變得更平滑;
令牌桶算法和漏桶算法則能夠塑形流量,讓流量更加平滑,但是令牌桶算法能夠應對一定的突發流量,所以在實際項目中應用更多。