我正在自定义Django的Admin Email Handler,并为其添加了一些突发保护,因此,如果在一分钟内出现多个错误,只会发送一封错误电子邮件。
def burst_protection(self, lag=60):
"""
:param lag:
:return:
"""
current_time = int(time.time())
global timestamp
if current_time - timestamp > lag:
timestamp = current_time
enable_burst_protection = False
else:
enable_burst_protection = True
return enable_burst_protection
最初,我将timestamp实现为类变量,但这不能防止生产环境中的消息突发,因为我假设服务器上有多个线程或进程同时访问和写入timestamp 。是否有线程和进程安全的方式将时间戳值存储在Python / Django中?
我听说可以通过将时间戳记值存储在数据库中来实现,但是我宁愿避免为此访问数据库。
答案 0 :(得分:1)
有一种方法,但是:
为了提供线程安全,将为每个线程返回不同的缓存后端实例。
您可以使用缓存,如果您不想访问数据库,则可以进行内存缓存存储。
https://docs.djangoproject.com/en/2.2/topics/cache/
查看本地内存缓存部分
示例(来自文档):
settings.py
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
然后您可以使用低级缓存,在同一页面部分低级缓存API
views.py-或连发对象所在的位置
from django.core.cache import caches
cache1 = caches['unique-snowflake'] # we get our cache handle
cache1.set('my_key', time())
...
cache1.get('my_key')
答案 1 :(得分:1)
Redis非常适合速率限制实施,例如:
我们需要每个用户一个唯一的密钥,在这里我们使用会话密钥或用户的ip地址+用户代理字符串的哈希,但是如果您想对全局进行限制,则返回一个常数(例如{ {1}}参数)将执行以下操作:
servicename
然后可以将速率限制器实现为装饰器:
import hashlib
def _ratelimit_key(servicename, request):
"""Return a key that identifies one visitor uniquely and is durable.
"""
sesskey = request.session.session_key
if sesskey is not None:
unique = sesskey
else:
ip = request.get_host()
ua = request.META.get('HTTP_USER_AGENT', 'no-user-agent')
digest = hashlib.md5(ua).hexdigest()
unique = '%s-%s' % (ip, digest)
return '%s-%s' % (servicename, unique)
用法:
import time
from django.conf import settings
from django import http
import redis
def strict_ratelimit(name, seconds=10, message="Too many requests."):
"""Basic rate-limiter, only lets user through 10 seconds after last attempt.
Args:
name: the service to limit (in case several views share a service)
seconds: the length of the quiet period
message: the message to display to the user when rate limited
"""
def decorator(fn):
def wrap(request, *args, **kwargs):
r = redis.Redis()
key = _ratelimit_key(name, request)
if r.exists(key):
r.expire(key, seconds) # refresh timeout
return http.HttpResponse(message, status=409)
r.setex(key, seconds, "nothing")
return fn(request, *args, **kwargs)
return wrap
return decorator
严格的速率限制器通常不是您想要的,但是,通常,您希望允许人们进行小的突发事件(只要在一个时间间隔内没有太多)。 “泄漏存储桶”(google it)算法可以做到这一点(与上面的用法相同):
@strict_ratelimit('search', seconds=5)
def my_search_view(request):
...