目錄
當系統處理能力有限,如何組織計畫外的請求對系統施壓。首先我們先看下一些簡單的限流策略,防止暴力攻擊。比如要對ip訪問,沒5s只能訪問10次,超過進行攔截。
如上圖,一般使用滑動視窗來統計區間時間內的訪問次數。
使用 zset 記錄 ip 訪問次數,每個 ip 通過 key 儲存下來,score 儲存當前時間戳,value 唯一用時間戳或者uuid來實現
**實現
public class redislimitertest
/*** @param ipaddress ip位址
* @param period 特定的時間內,單位秒
* @param maxcount 最大允許的次數
* @return
*/public boolean isiplimit(string程式設計客棧 ipaddress, int period, int maxcount)
public static void main(string args) }}
執行結果
訪問第1次, 結果:允許訪問
訪問第2次, 結果:允許訪問
訪問第3次, 結果:允許訪問
訪問第4次, 結果:允許訪問
訪問第5次, 結果:允許訪問
訪問第6次, 結果:限制訪問
訪問第7次, 結果:限制訪問
... ...
缺點:要記錄時間視窗所有的行為記錄,量很大,比如,限定60s內不能超過100萬次這種場景,不太適合這樣限流,因為會消耗大量的儲存空間。
示例**
public class funnellimitertest
void makespace()
if (deltaquota < 1)
this.leftquotjrrvea += deltaquota; // 漏斗剩餘空間 = 漏斗剩餘空間 + 騰出的空間
this.leakingts = nowts;
if (this.leftquota > this.capacity)
}boolean watering(int quota)
return false;}}
// 所有的漏斗
private map funnels = new hashmap<>();
/*** @param capacity 漏斗容量
* @param leakingrate 漏嘴流水速率 quota/s
*/public boolean isiplimit(string ipaddress, int capacity, float leakingrate)
return !funnel.watering(1); // 需要1個quota
}public static void main(string args) throws exception}}
執行結果
訪問第1次, 結果:允許訪問 # 第1次,容量剩餘2,執行後1
訪問第2次, 結果:允許訪問 # 第2次,容量剩餘1,執行後0
訪問第3次, 結果:允許訪問 # 第3次,由於過了2s, 漏斗流水剩餘1個空間,所以容量剩餘1,執行後0
訪問第4次, 結果:限制訪問 # 第4次,過了1s, 剩餘空間小於1, 容量剩餘0
訪問第5次, 結果:允許訪問 # 第5次,由於過了2s, 漏斗流水剩餘1個空間,所以容量剩餘1,執行後0
訪問第6次, 結果:限制訪問 # 以此類推.程式設計客棧..
訪問第7次, 結果:允許訪問
訪問第8次, 結果:限制訪問
訪問第9次, 結果:允許訪問
訪問第10次, 結果:限制訪問
我們觀察 funnel 物件的幾個字段,我們發現可以將 funnel 物件的內容按欄位儲存到乙個 hash 結構中,灌水的時候將 hash 結構的字段取出來進行邏輯運算後,再將新值回填到 hash 結構中就完成了一次行為頻度的檢測。
但是有個問題,我們無法保證整個過程的原子性。從 hash 結構中取值,然後在記憶體裡運算,再回填到 hash 結構,這三個過程無法原子化,意味著需要進行適當的加鎖控制。而一旦加鎖,就意味著會有加鎖失敗,加鎖失敗就需要選擇重試或者放棄。
如果重www.cppcns.com試的話,就會導致效能下降。如果放棄的話,就會影響使用者體驗。同時,**的複雜度也跟著公升高很多。這真是個艱難的選擇,我們該如何解決這個問題呢?redis-cell 救星來了!
redis 4.0 提供了乙個限流 redis 模組,它叫 redis-cell。該模組也使用了漏斗演算法,並提供了原子的限流指令。
該模組只有1條指令cl.throttle,它的引數和返回值都略顯複雜,接下來讓我們來看看這個指令具體該如何使用。
> cl.throttle key:*** 15 30 60 1
15 : 15 capacity 這是漏斗容量
30 60 : 30 operations / 60 seconds 這是漏水速率
1 : need 1 quota (可選引數,預設值也是1)
> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示允許,1表示拒絕
2) (integer) 15 # 漏斗容量capacity
3) (integer) 14 # 漏斗剩餘空間left_quota
4) (integer) -1 # 如果拒絕了,需要多長時間後再試(漏斗有空間了,單位秒)
5) (integer) 2 # 多長時間後,漏斗完全空出來(left_quota==capacity,單位秒)
在執行限流指令時,如果被拒絕了,就需要丟棄或重試。cl.throttle 指令考慮的非常周到,連重試時間都幫你算好了,直接取返回結果陣列的第四個值進行 sleep 即可,如果不想阻塞執行緒,也可以非同步定時任務來重試。
《redis深度歷險 核心原理與應用實踐》_錢文品
Redis 如何實現限流功能?
限流 這種事在生活中很常見,比如逢年過節時景點的限流,還有工作日的車輛單雙號限流等,有人可能會問為什麼要限流?我既然買了車子你還不讓我上路開?還有我倒景點買了門票,景點不是能賺更多的錢嗎?為什麼要限流呢?其實限流的主要目的就是為了保證整個系統的正常執行,比如以車輛限流為了,它的作用主要有兩個,乙個是...
幾種限流演算法
思路很簡單,請求先進入到漏桶裡,漏桶以固定的速度出水,也就是處理請求,當水加的過快,則會直接溢位,也就是拒絕請求,可以看出漏桶演算法能強行限制資料的傳輸速率。但是對於很多場景來說,除了要求能夠限制資料的平均傳輸速率外,還要求允許某種程度的突發傳輸。這時候漏桶演算法可能就不合適了,令牌桶演算法更為適合...
10 Redis實現限流功能
限流 這種事情即使在生活中也很常見,比如我們銀行辦理業務,銀行不可能給去的所有人同時服務,因為櫃檯就那麼幾個。所以可能一次只給5個人辦理業務,其他的人只能在後面排隊 再比如打飯等等,也是一樣的道理。因為能提供服務的數量有限,所以必須要通過限流的方式。這裡提一下微博,微博因為哪個明星出軌了,或者哪個明...