@鄭昀匯總
建立日期:20120925
關鍵詞索引:令牌桶演算法,漏桶演算法
背景:
防註冊機、秒殺器或掃號等常見電商流量過濾技術,一般具有如下要求:1) 高效能。演算法簡單高效,能對HTTP Requests進行即時線上處理。2) 分類錯誤率低。尤其是盡量保證不誤殺正常顧客訪問。3) 魯棒性強。由於雙方攻防的對抗性很強,所以演算法必須適應各種類型的攻擊情形(包括DDoS攻擊)。
課題1:對網站某一個URL/表單提交/Ajax請求的訪問進行即時檢測,找出過於頻繁請求的ip,對這些ip的訪問頻率進行限制。
課題2:對網站開放平台訪問,對某一個開放介面的調用,有頻次約束,即針對單一App Key不得超過每小時150次調用。
翻譯一下:鄭昀認為,我們希望限制住的是,在用M度量的任何時間周期內,某一個動作(action)的發生次數N。
英文關鍵詞:rate limiterrate limitingthrottle limiter
要控制的是 Average Rate :
解決思路:推薦採用令牌桶演算法的簡易實現。
參考資料:一)Leaky Bucket,漏桶演算法。 圖1.1 漏桶演算法1所示,桶本身具有一個
恒定的速率往下漏水,而上方
時快時慢地會有水進入桶中。當桶還未滿時,上方的水可以加入。一旦水滿,上方的水就無法加入了。桶滿正是演算法中的一個的關鍵觸發條件(即流量異常判斷成立的條件)。在桶滿水之後,常見的兩種處理方式為:1)暫時攔截住上方水的向下流動,等待桶中的一部分水漏走後,再允許存取上方水。2)溢出的上方水直接拋棄。將水看作網路通訊中資料包的抽象,則方式1起到的效果稱為Traffic Shaping,方式2起到的效果稱為Traffic Policing(流量策略)。由此可見,Traffic Shaping 的核心理念是“等待”,Traffic Policing 的核心理念是“丟棄”。它們是兩種常見的流速控制方法。 再回顧一下上面的圖,可以看出演算法只需要兩個參數:1)桶漏水的速率2)桶的大小 演算法核心:利用桶模型判斷何時的流量達到異常了 外延:1)流量異常時的處理方法:traffic policing v.s. traffic shaping2)處理的資料包是否定長:定長 v.s. 變長3)桶的大小是否等同於每個tick允許存取的水量:as a queue v.s. as a meter 二)Token Bucket,令牌桶演算法是網路流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種演算法。漏桶演算法不夠靈活,因此加入令牌機制。 基本思想:令牌桶在 traffic shaping 中的應用思想如2.1所示。 圖2.1 CAR和CTS進行流量控制
我們主要關注約定訪問速率(CAR)模式,即:
a. 按特定的速率向令牌桶投放令牌;
b.根據預設的匹配規則先對報文進行分類,不符合匹配規則的報文不需要經過令牌桶的處理,直接發送;
c.符合匹配規則的報文,則需要令牌桶進行處理。當桶中有足夠的令牌則報文可以被繼續發送下去,同時令牌桶中的令牌量按報文的長度做相應的減少;
d.當令牌桶中的令牌不足時,報文將不能被發送(即丟棄),只有等到桶中產生了新的令牌,報文才可以發送。這就可以限制報文的流量只能是小於等於令牌產生的速度,達到限制流量的目的。
實現:
在資料結構上,沒有必要真的實現一個令牌桶。
基於時間的流逝產生受控制數量的令牌即可——以時間的流逝來洗滌舊跡,也就是將兩次發包或者收包的間隔和令牌數量聯絡起來。 輔助理解的圖形: 令牌桶和漏桶演算法最主要的差別在於:
漏桶演算法能夠強行限制資料的傳輸速率,而令牌桶演算法能夠在限制資料的平均傳輸速率的同時還允許某種程度的突發傳輸。 在令牌桶演算法中,只要令牌桶中存在令牌,那麼就允許突發地傳輸資料直到達到使用者配置的門限,因此它適合於具有突發特性的流量。 三)http://developer.linkedin.com/documents/throttle-limits 這是常見的開放平台限制請求速率的方式。LinkedIn 比較好的一點就是把
Application throttles和
Developer throttles分開了。後者是方便聯調測試的。 四)Better Rate Limiting in .NET [Penned Objects] 五)鄭昀推薦閱讀,Leaky Bucket演算法和Token Bucket演算法學習筆記,令牌桶演算法的應用,QoS令牌桶工作原理,用Netfilter模組實現基於令牌桶的每IP地址流量控制 六)Token Bucket 演算法的 Python 實現一:kombu.utils.limits.py代碼:https://github.com/celery/kombu/blob/master/kombu/utils/limits.py對此實現一個較為早期的解釋:http://code.activestate.com/recipes/511490/即,每次外界調用 _get_tokens 方法時,才會查一下需要追加多少token。class TokenBucket(object): def _get_tokens(self): if self._tokens < self.capacity: now = time.time() delta = self.fill_rate * (now - self.timestamp) self._tokens = min(self.capacity, self._tokens + delta) self.timestamp = now return self._tokens消耗令牌則是通過 consume 函數,指明本次消耗多少張令牌:
def consume(self, tokens): """Consume tokens from the bucket. Returns True if there were sufficient tokens otherwise False.""" if tokens <= self.tokens: self._tokens -= tokens else: return False return True
七)Rate Limiting 的 memcache-based django decorator 實現:Rate limiting with memcached代碼實現:https://github.com/simonw/ratelimitcache/blob/master/ratelimitcache.py 八)Preventing login hacks: Rate limiting using memcachedPython 實現。它明確提出了防字典攻擊防掃號的目的。既可限制住ip,也可限制住其他欄位如 username 。 八)Token Bucket 演算法的node.js實現jhurliman/node-rate-limiter 給出了一個非常便於理解的 Token 消耗方式:下面是 150次請求/次 範例,每1次請求消耗1個token:
var RateLimiter = require('limiter').RateLimiter;// Allow 150 requests per hour (the Twitter search limit). Also understands// 'second', 'minute', 'day', or a number of millisecondsvar limiter = new RateLimiter(150, 'hour');// Throttle requestslimiter.removeTokens(1, function(err, remainingRequests) { // err will only be set if we request more than the maximum number of // requests we set in the constructor // remainingRequests tells us how many additional requests could be sent // right this moment callMyRequestSendingFunction(...);});
下面是150KB/sec 範例,每1個位元組的傳輸就消耗1個token:
var BURST_RATE = 1024 * 1024 * 150; // 150KB/sec burst ratevar FILL_RATE = 1024 * 1024 * 50; // 50KB/sec sustained ratevar TokenBucket = require('limiter').TokenBucket;// We could also pass a parent token bucket in as the last parameter to// create a hierarchical token bucketvar bucket = new TokenBucket(BURST_RATE, FILL_RATE, 'second', null);bucket.removeTokens(myData.byteLength, function() { sendMyData(myData);});
九)StackOverflow 上的相關討論:Throttling method calls to M requests in N secondsWhat’s a good rate limiting algorithm?Best way to implement request throttling in ASP.NET MVC?