最后我采用了不同的方法。我在应用程序代码中添加了一个限制器来处理这个问题。
"""Concurrency requests limiter
Inspired by Flask-Limiter
"""
from collections import defaultdict
from threading import BoundedSemaphore
from functools import wraps
from flask import request
from werkzeug.exceptions import TooManyRequests
# From flask-limiter
def get_remote_address():
"""Get IP address for the current request (or 127.0.0.1 if none found)
This won't work behind a proxy. See flask-limiter docs.
"""
return request.remote_addr or '127.0.0.1'
class NonBlockingBoundedSemaphore(BoundedSemaphore):
def __enter__(self):
ret = self.acquire(blocking=False)
if ret is False:
raise TooManyRequests(
'Only {} concurrent request(s) allowed'
.format(self._initial_value))
return ret
class ConcurrencyLimiter:
def __init__(self, app=None, key_func=get_remote_address):
self.app = app
self.key_func = key_func
if app is not None:
self.init_app(app)
def init_app(self, app):
self.app = app
app.extensions = getattr(app, 'extensions', {})
app.extensions['concurrency_limiter'] = {
'semaphores': defaultdict(dict),
}
def limit(self, max_concurrent_requests=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Limiter not initialized
if self.app is None:
return func(*args, **kwargs)
identity = self.key_func()
sema = self.app.extensions['concurrency_limiter'][
'semaphores'][func].setdefault(
identity,
NonBlockingBoundedSemaphore(max_concurrent_requests)
)
with sema:
return func(*args, **kwargs)
return wrapper
return decorator
limiter = ConcurrencyLimiter()
def init_app(app):
"""Initialize limiter"""
limiter.init_app(app)
if app.config['AUTHENTICATION_ENABLED']:
from h2g_platform_core.api.extensions.auth import get_identity
limiter.key_func = get_identity
然后我需要做的就是把这个装饰应用到我的视图中:
@limiter.limit(1) #Â One concurrent request by user
def get(...):
...
实际上,我只保护那些产生高流量的。
在应用程序代码中这样做很好,因为我可以为每个经过身份验证的用户而不是每个IP设置一个限制。
为此,我只需要替换默认值
get_remote_address
在里面
key_func
返回用户唯一标识的函数。
注意,这为每个视图函数设置了不同的限制。如果限制需要是全局的,可以用不同的方法实现。事实上,这会更简单。