2010-09-05 69 views
4

我正在尋找一種有效的方法來限制從Google App Engine向第三方服務的請求。第三方服務費率按每個帳戶限制請求,並且在Google App Engine方面,大部分工作都是在任務內執行的。令牌桶在這裏是一個很好的通用算法。基於Memcache的限速算法? (令牌桶?)

問:有什麼方法可以有效地對每個帳戶的請求進行速率限制,而不是每個服務的限制?

這不應該涉及在GAE任務隊列上設置費率,因爲每個賬戶的請求數量和服務賬戶數量會有很大差異。出於性能原因,我最喜歡基於memcache(incr/decr?)的想法!

我認爲這歸結爲基於memcache的令牌桶?

想法?

回答

1

我知道這是一個老問題,但它是一個頂級的搜索結果,我認爲其他人可能會找到一個替代方案,使我變得有用。這比上面的解決方案有點更精細(精確到秒),簡單(只有一個單一的功能),以及高性能的(只有一個內存緩存查找):

import webapp2 
from functools import wraps 
from google.appengine.api import memcache 


def rate_limit(seconds_per_request=1): 
    def rate_limiter(function): 
    @wraps(function) 
    def wrapper(self, *args, **kwargs): 
     added = memcache.add('%s:%s' % (self.__class__.__name__, self.request.remote_addr or ''), 1, 
          time=seconds_per_request, namespace='rate_limiting') 
     if not added: 
     self.response.write('Rate limit exceeded.') 
     self.response.set_status(429) 
     return 
     return function(self, *args, **kwargs) 
    return wrapper 
    return rate_limiter 


class ExampleHandler(webapp2.RequestHandler): 
    @rate_limit(seconds_per_request=2) 
    def get(self): 
    self.response.write('Hello, webapp2!') 
+0

不幸的是,你的反應不實現令牌桶速率限制爲每管理員的要求。請參閱https://github.com/bradbeattie/django-cache-throttle/blob/master/cache_throttle/utils.py,瞭解我的這種算法的Python實現,並參閱http://en.wikipedia.org/wiki/Token_bucket有關令牌桶的更多信息。 – 2014-04-08 18:33:44

+0

呃,我把令牌桶的東西當作比需求更多的建議。此外,這主要是針對其他不需要令牌桶的人,因爲該頁面仍然是「App Engine費率限制」的頂級SERP。 出於好奇,令牌桶提供了什麼優勢比我的方法?它似乎能夠更好地處理「突發」,但是我還沒有想過其他優點嗎?只需稍作修改即可允許自定義速率限制鍵。 – 0x24a537r9 2014-05-07 04:47:29

+1

是的,你是對的錢,因爲它主要是處理突發事件。假設你想在一天中允許用戶24行動。你可以將它們限制爲每小時一次,但是如果他們現在想要消耗24個,那麼它們就會使它們更加緊張。我認爲,令牌桶解決方案更符合預期的行爲。 – 2014-05-09 17:43:56

1

這裏是我是如何實現令牌桶的memcache上GAE:

編輯:以(另一個)刺傷此。

這部分地借用https://github.com/simonw/ratelimitcache/blob/master/ratelimitcache.py

def throttle(key, rate_count, rate_seconds, tries=3): 
    ''' 
    returns True if throttled (not enough tokens available) else False 
    implements token bucket algorithm 
    ''' 
    client = memcache.Client(CLIENT_ARGS) 
    for _ in range(tries): 
     now = int(time.time()) 
     keys = ['%s-%s' % (key, str(now-i)) for i in range(rate_seconds)] 
     client.add(keys[0], 0, time=rate_seconds+1) 
     tokens = client.get_multi(keys[1:]) 
     tokens[keys[0]] = client.gets(keys[0]) 
     if sum(tokens.values()) >= rate_count: 
      return True 
     if client.cas(keys[0], tokens[keys[0]] + 1, time=rate_seconds+1) != 0: 
      return False 
    logging.error('cache contention error') 
    return True 

下面是用法示例:

def test_that_it_throttles_too_many_requests(self): 
    burst = 1 
    interval = 1 
    assert shared.rate_limit.throttle('test', burst, interval) is False 
    assert shared.rate_limit.throttle('test', burst, interval) is True 


def test_that_it_doesnt_throttle_burst_of_requests(self): 
    burst = 16 
    interval = 1 
    for i in range(burst): 
     assert shared.rate_limit.throttle('test', burst, interval) is False 
    time.sleep(interval + 1) # memcache has 1 second granularity 
    for i in range(burst): 
     assert shared.rate_limit.throttle('test', burst, interval) is False