常見的限流算法redis
顧名思義,計數器算法是指在必定的時間窗口內容許的固定數量的請求.好比,2s內容許10個請求,30s內容許100個請求等等.若是設置的時間粒度越細,那麼相對而言限流就會越平滑,控制的粒度就會更細.算法
試想,若是設置的粒度比較粗會出現什麼樣的問題呢?以下圖設置一個 1000/3s 的限流計數統計.
服務器
圖中的限流策略爲3s內容許的最大請求量爲1000,那麼會出現2個極端:
極端狀況1:
第1s請流量爲10,
第2s請流量爲10,
第3s請流量忽然激增到980.這意味着在這一刻,有大量的請求蜂擁而至,假設服務每秒能處理的上線爲800/1s,可是此刻卻有超過這個量級的請求量,那麼後果是不堪設想的.spa
極端狀況2:
第1s請流量忽然就達到990,
留給後續第2s,3s的可請求數量就很是少了,可能會出現大量的拒絕請求.設計
結論:
若是用統計計數算法,儘可能保持粒度切割精細.code
redis的ttl特性完美的知足了這一需求,將時間窗口設置爲key的失效時間,而後將key的值每次請求+1便可.僞代碼實現思路:blog
//1.判斷是否存在該key if(EXIT(key)){ // 1.1自增後判斷是否大於最大值,並返回結果 if(INCR(key) > maxPermit){ return false; } return true; } //2.不存在key,則設置key初始值爲1,失效時間爲3秒 SET(KEY,1); EXPIRE(KEY,3);
漏桶算法核心概念:資源
缺點:
不難想象漏桶算法並不能很好的應對突發的流量限制,在某一個時間段流量激增,則漏桶算法處理就比較無能爲力.這個時候就須要用到和他相反設計的令牌桶算法it
如上圖所示,整個請求流程一目瞭然.簡單歸納以下:ast
1.用戶請求資源時首選從桶裏獲取令牌,若是有令牌則放行,如此同時桶裏的令牌數量-1
2.於此同時,以必定的速率往桶裏加入令牌,這個速度是可根據實際場景隨意設置.
var key; var maxPermit;//桶的容量,即最大請求限制 var expire;//失效時間 var bucketInterval;//每次向桶裏添加令牌的時間間隔 var bucketNum;//每次向桶裏添加令牌的個數 var lastTimeKey = key +"last";//標記上一次操做時間 //判斷是否存在該key if(EXIT(key)){ var value = GET(key); var diffTime = now() - lastTimeKey; // 1.1判斷是否超出時間間隔 if(diffTime > bucketInterval){ // 1.2根據時間間隔,計算出應該向桶裏添加令牌的個數 local maxValue = value+math.floor(diff/interval)*step; if (maxValue > limit) value = limit; else value = maxValue; //設置key的值及操做時間 SET(key,value); SET(lastTimeKey,now()); } // 2.1在時間間隔內,判斷桶裏是否有值 if(value <= 0){ reurn false; }else{ // 2.2 減1 DECR(key); } reture true; } //2.不存在key,則設置key初始值爲maxPermit-1 SET(key,maxPermit-1); EXPIRE(lastTimeKey,now());
上面實現代碼只是僞代碼,提供的是一種思路而已. 仔細想來其中某個環節其實並不完美.你們能夠參考Guava的ratelimit實現思路,他的限流就是基於令牌桶算法,可是比較遺憾的是在單機下的限流.
思考:
就是時間間隔若是過長的話,一次性向桶裏添加的令牌數量則是桶的最大容量!那麼某個時間的瞬間請求過來,服務器的壓力是很是大的.
因此此處增長令牌數能夠設置的稍微合理些,哪怕間隔時間再長!
代碼稍後上傳