Основанный на Memcache алгоритм ограничения скорости? (Токен ведро?)
Я ищу эффективный подход к запросу ограничения скорости от Google App Engine к стороннему сервису. Сторонние тарифы на обслуживание ограничивают количество запросов для каждого аккаунта, а на стороне Google App Engine большая часть работы выполняется внутри задач. Сегменты токенов - это отличный общий алгоритм.
Вопрос: какой подход можно использовать для эффективного ограничения запросов на уровне отдельных аккаунтов, а не отдельных сервисов?
Это не должно включать настройку тарифов в очередях задач GAE, так как количество запросов на учетную запись и количество обслуживаемых учетных записей будут сильно различаться. Из соображений производительности меня больше всего интересуют идеи на основе memcache (incr/decr?)!
Я думаю, что это сводится к ведению токенов на основе memcache?
Мысли?
3 ответа
Я знаю, что это старый вопрос, но это лучший результат поиска, и я подумал, что другие могут найти альтернативу, которую я сделал полезной. Это немного более детально (вплоть до второго), просто (только одна функция) и производительность (только один поиск в memcache), чем решение выше:
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!')
Я сохранил этот проект как закладку некоторое время назад: http://code.google.com/p/gaedjango-ratelimitcache/
Не совсем ответ на ваш конкретный вопрос, но, возможно, это поможет вам начать.
Вот как я реализовал Token Bucket с 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