基于Memcache的限速算法? (令牌桶?)

发布于 2024-09-18 08:25:15 字数 309 浏览 10 评论 0原文

我正在寻找一种有效的方法来限制从 Google App Engine 到第三方服务的请求速率。第三方服务速率限制每个帐户的请求,而在 Google App Engine 方面,大部分工作是在任务内部执行的。令牌桶在这里是一个很好的通用算法。

问:可以使用什么方法来有效地对每个帐户而不是每个服务的请求进行速率限制?

这不应该涉及在 GAE 任务队列上设置速率,因为每个帐户的请求数量和服务的帐户数量会有很大差异。出于性能原因,我对基于内存缓存(incr/decr?)的想法最感兴趣!

我认为这可以归结为基于memcache的令牌桶?

想法?

I'm looking for an efficient approach to rate-limiting request from Google App Engine to a third party service. The third party service rate limits requests on a per-account basis, and on the Google App Engine side, most of the work is carried out inside tasks. Token buckets are a great general algorithm here.

Q: what approach can be used to efficiently rate-limit requests on a per-account rather than per-service basis?

This should not involve setting up rates on GAE task queues as the number of requests per account and the number of accounts serviced will vary greatly. For performance reason I'm most interested in memcache-based (incr/decr?) ideas!

I think this boils down to memcache-based token bucket?

Thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜心 2024-09-25 08:25:15

我不久前将此项目保留为书签:http://code.google.com/ p/gaedjango-ratelimitcache/

并不是您具体问题的真正答案,但也许它可以帮助您入门。

I kept this project as a bookmark a while ago : http://code.google.com/p/gaedjango-ratelimitcache/

Not really an answer to your specific question but maybe it could help you get started.

娜些时光,永不杰束 2024-09-25 08:25:15

我知道这是一个老问题,但它是热门搜索结果,我认为其他人可能会找到我制作的有用的替代方案。与上面的解决方案相比,它更精细(精确到第二个)、更简单(只有一个函数)和更高性能(只有一个内存缓存查找):

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!')

I know this is an old question, but it's a top search result and I thought others might find an alternative I made useful. It's a bit more granular (down to the second), simple (only a single function), and performant (only one memcache lookup) than the solution above:

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!')
云淡月浅 2024-09-25 08:25:15

以下是我在 GAE 上使用 memcache 实现令牌桶的方法:

编辑:对此进行(另一个)尝试。

这部分借自 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

Here is how I implemented token bucket with memcache on GAE:

Edit: taking (another) stab at this.

This is borrowed in part from 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

Here are usage examples:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文