高可用實踐之服務限流(一)

2021-09-12 01:30:52 字數 3452 閱讀 6375

令牌桶演算法

漏桶演算法

分布式限流

實戰應用

參考在流量高峰期或因為活動、熱點事件、惡意攻擊等原因導致流量突增時,通過對請求流量速率進行限制,保證請求流量在合理範圍內,避免因為超出預期的大流量導致服務整體效能下降、響應緩慢或不可用。在觸發限流後,可以通過拒絕部分服務、等待、排隊、降級等策略保護業務系統。

定義:限制固定時間段內,特定api的最大請求量,如限制每秒最多請求100次,超出100次觸發限流策略。

優點:實現簡單直觀

缺點:

請求分布不均,不能平滑流量,容易出現部分時刻「服務不可用」的表現。如1秒內,前10ms請求100次,導致後990ms全部請求被拒絕。即請求分布不均,部分請求在某個時刻聚集,導致這個時刻所屬的時間視窗內的其他時刻被拒絕。

出現流量尖峰,容易導致單位時間超出伺服器預期負載,如預期單位時間1s內請求100次,上一秒的最後10ms請求100次,下一秒的最早10ms請求100次,則在單位時間1秒內,出現200次請求,超出服務承載預期。

將乙個時間區間劃分為n個長度固定的小時間視窗,每一次請求考察當前時間往前n個連續時間視窗的請求量總和,如果超過閾值則觸發限流策略。如配置請求閾值100,將1s時間劃分為5個連續的時間視窗,每個視窗長度250ms,則意味這上一秒當前250ms加上一秒的最後750ms請求量不會100個請求。

解決了固定視窗流量尖峰的問題,確保在任意時刻,過去視窗時間內的請求不會超出閾值。

不能解決請求分布不均的問題,即無法平滑流量

實現更複雜,需要維護時間視窗,占用記憶體更多,計算時間複雜度也相應變大。

存在乙個令牌桶計數器,每隔一定時間放入一塊令牌,每次請求取出一塊令牌。,令牌桶有最大容量限制,當計數器令牌數達到最大值,則不再放入,如果計數器令牌數為0,請求則觸發限流策略。如每隔10ms放入一塊令牌,最大令牌數為100,假設某個時刻令牌桶放滿令牌,則這個時刻的前10ms能請求100次,清20ms能請求101次。

解決了固定視窗流量尖峰的問題,確保在任意時刻,過去視窗時間內的請求不會超出閾值。

可以有效平滑流量,因為令牌桶的令牌是勻速放入的

相對滑動視窗更節省記憶體

實現複雜,時間複雜度高

令牌桶預熱

相對於令牌桶是固定速率放入令牌,在沒有令牌的時候拒絕請求,漏桶則是固定資料漏出請求,當請求量過大,流入請求超過桶容量,則觸發限流策略。具體實現時,可以將漏桶想象成乙個有流量限制的先進先出的佇列,在佇列不為空情況下,每隔一定時間取出乙個佇列請求進行處理相應,如果佇列已經滿了,再來請求則觸發限流相關策略。如限制每10ms漏出乙個請求,桶容量為100個請求,則每10ms最多能處理乙個請求。

實現相對簡單,可以限**務請求速率,並且穩定在乙個常速。

對於特發流量處理效率過低,在沒有到達伺服器負載閾值,也只能序列處理請求。

可以基於redis實現分布式限流。

我們可以基於redis來實現固定視窗的分布式限流演算法:

可以為每個請求路徑配置乙個有超時時間的計數器,每次請求給計數器加一,如果超過限制閾值,則返回限流標識,具體實現如下:

// 限流桶

private

static

final string bucket =

"bucket"

;/**

* 基於redis的固定視窗分布式限流

* @param point 限流方法標識

* @param limit 限流閾值

* @param timeout 固定時間視窗大小

* @return 如果觸發限流,返回-1,否則返回當前請求在固定視窗裡的請求計數

*/public

long

acquiretokenfrombucket

(string point,

int limit,

int timeout)

if(counter <=1)

return counter;

}finally

}

基於滑動視窗的實現可以利用redis的sortedset,以時間為分數來維護乙個滑動視窗,每次請求時,清理滑動視窗之前的計數,而後嘗試在視窗內,以乙個單調遞增的計數插入乙個記錄,並計算其排名,如果排名超過限流閾值,說明當前請求在滑動視窗內超過請求次數閾值,則觸發限流。

在具體演算法中,我們維護了三個變數:

bucket:任意時刻維護乙個滑動視窗大小的限流桶,在redis中以sortedset實現,分數是乙個遞增計數,可以表示在滑動視窗內的請求順序(排名)

bucket_count:維護乙個遞增計數,每次觸發一次請求,會給計數器加一,用來標識當前請求的順序。

bucket_monitor:底層也是乙個sortedset實現,和bucket不同的是,bucket_monitor的分數為時間戳,用於滑動視窗的邊界計算,每次呼叫會先根據分數清理bucket_monitor滑動視窗外的請求記錄,而後與bucket取交集,來完成對真正的限流桶bucket的滑動視窗清理。

具體限流演算法如下:

// 限流桶

private

static

final string bucket =

"bucket"

;// 請求桶計數

private

static

final string bucket_count =

"bucket_count"

;// 用於記錄請求時間,在超時後請求請求記錄,只保留滑動視窗內的請求記錄

private

static

final string bucket_monitor =

"bucket_monitor"

;/**

* 基於redis的固定視窗分布式限流

* @param point 限流方法標識

* @param limit 限流閾值

* @param timeout 滑動視窗大小

* @return 如果觸發限流,返回null,否則返回當前請求標識作為token

*/public string acquiretokenfrombucket

(string point,

int limit,

long timeout)

else

// 返回null,表示獲取token失敗

return null;

}finally

}

在實際處理中,需要額外以計數為分數的桶bucket,而非直接以時間戳標識請求順序的bucket_monitor作為限流桶,主要是考慮到在分布式環境中,不同機器的時鐘可能不一致,如果某一機器的時鐘偏早,可能會導致它的排名考前,但實際已經到達限流閾值,導致限流不夠精確。

基於特定的限流演算法,我們有以下基於具體應用場景的限流策略:

服務限流:通過正則或字元匹配,限制特定服務方法的單位時間請求流量。

單機限流:限制單台機器的單位時間服務請求流量

集群限流:限制特定集群多台機器的單位時間服務請求流量

高可用之降級 限流 拒絕服務

reference 系統的 可 建設,它其實是 個系統 程,需要考慮到系統建設的各個階段,也就是說它其實貫穿了系統建設的整個 命週期,如下圖所 具體來說,系統的 可 建設涉及架構階段 編碼階段 測試階段 發布階段 運 階段,以及故障發 時。1.架構階段 架構階段主要考慮系統的可擴充套件性和容錯性,要...

Keepalived高可用集群實踐

1 實踐的硬體環境準備 準備4臺物理伺服器或者4臺vm虛擬機器,其中兩台用來做keepalived伺服器,兩台做web測試站點 hostname i p 解 釋 lb01 10.0.0.7 keepalived主伺服器 nginx主負載均衡器 lb02 10.0.0.8 keepalived備伺服器...

高併發高可用的架構實踐 設計理念(一)

客戶端頁面快取 http header中包含expires cache of control,last modified 304,server不返回body,客戶端可以繼續用cache,減少流量 etag 反向 快取 應用端的快取 memcache 記憶體資料庫 buffer cache機制 資料庫...