Django:RestFramework之-------频率控制
5.频率控制
5.1频率监控代码实现
import time
VISIT_RECORD = {}
class VisitThrottle(object):
def __init__(self):
#定义历史记录,生产环境上,应该将其保存在redis
self.history = []
def allow_request(self,request,view):
#1.获取用户IP
remote_addr = request._request.META.get("REMOTE_ADDR")
print(remote_addr)
#记录访问时的时间
ctime = time.time()
#判断当前IP是否在访问记录中
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
#拿取当前访问IP记录
history = VISIT_RECORD.get(remote_addr)
#将历史访问时间记录,复制给self.history,以方便wait方法调用,通过wait方法告诉用户一些信息
self.history = history
#逻辑判断只记录1分钟内访问记录。
while history and history[-1] < ctime-60:
history.pop()
#访问历史列表中时间记录不能大于3
if len(history) < 3:
history.insert(0,ctime)
return True
def wait(self):
"""用于提示多少秒后才可以重新访问"""
#记录当前时间
ctime = time.time()
#返回告知用户还有多长时间可以再次访问
return 60-(ctime - self.history[-1])
class AuthView(APIView):
#用于频率限制
throttle_classes = [VisitThrottle,]
def post(self,request,*args,**kwargs):
ret = {"code":1000,"msg":None}
return JsonResponse(ret)
-
效果:
当发送请求在监测频率范围内:
当发送请求超出监测频率范围:返回信息提示,此信息提示为wait定义的。
5.2频率表权限源码解析
def dispatch(self, request, *args, **kwargs):
...
try:
#执行initial方法
self.initial(request, *args, **kwargs)
...
#---------------------------------------------------------
def initial(self, request, *args, **kwargs):
...
self.check_throttles(request)#用于限制频率
...
#---------------------------------------------------------
def check_throttles(self, request):
throttle_durations = []
#遍历 循环get_throttles
for throttle in self.get_throttles():
#如果allow_request返回False,则拒绝访问,并执行wait方法,抛出提示用户信息
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait())
if throttle_durations:
self.throttled(request, max(throttle_durations))
#---------------------------------------------------------
def get_throttles(self):
#实例化返回一个列表,当用户定义throttle_classes,则返回用户定义的[对象,对象...]
#如果用户没有定义,将使用django内置配置:throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
return [throttle() for throttle in self.throttle_classes]
5.3全局和局部配置权限:
#全局配置settings.py
REST_FRAMEWORK={
'DEFAULT_THROTTLE_CLASSES':['app.utils.throttle.VisitThrottle']
}
#局部配置
类下面定义throttle_classes =[频率监测类,]
5.4内置频率类
class BaseThrottle:
def allow_request(self, request, view):
"""
如果应该允许请求,则返回True,否则返回False。
"""
raise NotImplementedError('.allow_request() must be overridden')
def get_ident(self, request):
"""
通过解析HTTP_X_FORWARDED_FOR(如果存在且代理数> 0)来识别发出请求的机器。如果不可用,则使用所有HTTP_X_FORWARDED_FOR(如果可用),如果不使用,则不使用REMOTE_ADDR
"""
xff = request.META.get('HTTP_X_FORWARDED_FOR')
remote_addr = request.META.get('REMOTE_ADDR')
num_proxies = api_settings.NUM_PROXIES
if num_proxies is not None:
if num_proxies == 0 or xff is None:
return remote_addr
addrs = xff.split(',')
client_addr = addrs[-min(num_proxies, len(addrs))]
return client_addr.strip()
return ''.join(xff.split()) if xff else remote_addr
def wait(self):
"""
(可选)返回建议的秒数,以等待下一个请求
"""
return None
#BaseThrottle定义了get_ident用于获取访问用户ID,这样可以自定义频率监测类继承BaseThrottle,通过调用父类方法,获取访问用户ID
remote_addr = self.get_ident(request)
#------------------------------------------------------------------------------------------------
class SimpleRateThrottle(BaseThrottle):
"""一个简单的缓存实现,只需要覆盖.get_cache_key()即可。速率(请求/秒)由View类的“ rate”属性设置。该属性是形式为'number_of_requests / period'的字符串。周期应为以下之一:('s','sec','m','min','h','hour','d','day')用于限制的先前请求信息存储在缓存中"""
cache = default_cache
timer = time.time
cache_format = 'throttle_%(scope)s_%(ident)s'
scope = None
THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
def __init__(self):
if not getattr(self, 'rate', None):
self.rate = self.get_rate()
#num_requests表示次数,duration表示秒/分数
self.num_requests, self.duration = self.parse_rate(self.rate)
def get_cache_key(self, request, view):
"""
应该返回一个唯一的可用于节流的缓存键。必须重写。如果不应该限制请求,则可能会返回“无”
"""
raise NotImplementedError('.get_cache_key() must be overridden')
def get_rate(self):
"""
确定允许请求速率
"""
if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
return self.THROTTLE_RATES[self.scope]
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
def parse_rate(self, rate):
"""
给定请求速率字符串
"""
if rate is None:
return (None, None)
num, period = rate.split('/')
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration)
def allow_request(self, request, view):
"""
Implement the check to see if the request should be throttled.
On success calls `throttle_success`.
On failure calls `throttle_failure`.
"""
if self.rate is None:
return True
# 将访问者信息放在缓存中
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
#从缓存拿历史记录, cache = default_cache为Django内置缓存
self.history = self.cache.get(self.key, [])
self.now = self.timer()
# Drop any requests from the history which have now passed the
# throttle duration
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success()
def throttle_success(self):
#成功返回
"""
Inserts the current request's timestamp along with the key
into the cache.
"""
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True
def throttle_failure(self):
#失败返回
"""
Called when a request to the API has failed due to throttling.
"""
return False
def wait(self):
"""
返回建议的下一个请求时间(以秒为单位)。
"""
if self.history:
remaining_duration = self.duration - (self.now - self.history[-1])
else:
remaining_duration = self.duration
available_requests = self.num_requests - len(self.history) + 1
if available_requests <= 0:
return None
return remaining_duration / float(available_requests)
#SimpleRateThrottle 内置分装好了监测频率各种(成功返回,错误返回,定义访问频次,返回下一次请求时间,确定速率等)方法。这样我们自己定义类继承SimpleRateThrottle
#更改自定义频率监测:
class VisitThrottle(SimpleRateThrottle):
scope = "key"#根据scope的值,去配置文件找频率设置
def get_cache_key(self, request, view):
return self.get_ident(request)
#settings.py配置
REST_FRAMEWORK={
'DEFAULT_THROTTLE_CLASSES':['app.utils.throttle.VisitThrottle'],
"DEFAULT_THROTTLE_RATES":{
#设置频率3/m表示每分钟最多访问3次,并给予标识
'key':'3/m'
}
}
5.5对当前登录用户进行操作
#settings.py配置
REST_FRAMEWORK={
'DEFAULT_THROTTLE_CLASSES':['app.utils.throttle.VisitThrottle'],
"DEFAULT_THROTTLE_RATES":{
'key':'3/m',
#用于登录用户频率监测配置设置
'vipkey':'10/m',
}
}
#throttle.py
#定义登录用户频次监测类
class UserThrottle(SimpleRateThrottle):
scope = "vipkey"
def get_cache_key(self, request, view):
#返回登录用户名
return request.user.username
#因定义登录用户检测的类,不能放入settings配置,应予以局部添加
class OrderView(APIView):
throttle_classes = [UserThrottle, ]
5.6小总结:
基本使用:
类 继承:BaseThrottle 实现:allow_request,wait 方法
类 继承:SimpleRateThrottle 实现:get_cache_key ,scope (配置文件中的key,去找频率设置)