代码使用
utils.throttle
VISIT_REMOTE = {}
from rest_framework.throttling import BaseThrottle
class VisttThrottle(BaseThrottle):
'''用户节流'''
def __init__(self):
self.history = None
def allow_request(self, request, view):
remote_addr = request.Meta.get('REMOTE_ADDR')
# remote_addr = request._request.META.get('REMOTE_ADDR')
# 当封装的request遇到自己没有的属性时,会到下面的的_request里面找
from time import time
ctime = time()
if remote_addr not in VISIT_REMOTE:
VISIT_REMOTE[remote_addr] = [ctime, ]
return True # 可以访问
history = VISIT_REMOTE.get(remote_addr)
# 如果访问过此网站, 就得到它访问的时间列表
self.history = history
while history and history[-1] < ctime - 60:
# 可以访问, 最后一个时间没用了,因为下一个访问的肯定更大,留一个小的值没意义
history.pop()
if len(history) <= 3:
return True
def wait(self):
ret = 60 - time() + self.history[-1]
return ret
局部使用
class OrderView(APIView):
throttle_classes = [节流类]
def get(self,request):
ret = {'code':10000,'msg':None}
try:
pass # 具体视图函数逻辑
except Exception as e:
pass # 抛出异常
return JsonResponse(ret)
全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['utils.throttling.Mythrottle'],
}
以上是最基本的自定义
- rest_framework中内置了访问频率的类
from rest_framework.throttling import SimpleRateThrottle
除此之外,还有其他的内置类
class BaseThrottle:...
class SimpleRateThrottle(BaseThrottle):...
class AnonRateThrottle(SimpleRateThrottle):...
class UserRateThrottle(SimpleRateThrottle):...
class ScopedRateThrottle(SimpleRateThrottle):...
SimpleRateThrottle源码剖析
- 看其源码,知其内容。
就拿简单的SimpleRateThrottle
当我们使用SimpleRateThrottle时,我们需要设置
THROTTLE_RATES
的值。
class SimpleRateThrottle(BaseThrottle):
"""
A simple cache implementation, that only requires `.get_cache_key()`
to be overridden.
The rate (requests / seconds) is set by a `rate` attribute on the View
class. The attribute is a string of the form 'number_of_requests/period'.
Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
Previous request information used for throttling is stored in the cache.
"""
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):
# 如果类中没有设置rate属性,就执行方法get_rate
if not getattr(self, 'rate', None):
self.rate = self.get_rate()
# 如果类中有rate属性的值 ,就调用rate的解析函数parse_rate
self.num_requests, self.duration = self.parse_rate(self.rate)
def get_cache_key(self, request, view):
"""
Should return a unique cache-key which can be used for throttling.
Must be overridden.
May return `None` if the request should not be throttled.
"""
# 返回一个缓存的key, 并且要是唯一标识
raise NotImplementedError('.get_cache_key() must be overridden')
def get_rate(self):
"""
Determine the string representation of the allowed request rate.
"""
# 如果rate属性的值得不到,scope属性的值也得不到,就抛出异常
if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
'''
'DEFAULT_THROTTLE_RATES':{
'MyThrottle':'3/m' 都是为了得到3/m这个值 代表1min中内访问3次,m代表分钟。
}
'''
# scope 就是DEFAULT_THROTTLE_RATES这个列表的 键
# rate 就是访问的频率,可以直接写,也可以用scope
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):
"""
Given the request rate string, return a two tuple of:
,
"""
if rate is None:
return (None, None)
num, period = rate.split('/')
# 由此可见,rate的类型为 num/period 根据不同情况,重写这个方法,将
# 下面的duration重写,就可以自定义频率了
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`.
"""
# 经过上面一轮操作后,如果rate还是None, 就代表没有访问频率的限制
if self.rate is None:
return True
# 用户的访问记录在缓存中, 一般ip作为唯一标识,也就是作为key ,这里就是得到这个key
self.key = self.get_cache_key(request, view)
# 如果这个key不存在, 那么就代表这个还没访问过此网站, 所以可以继续访问 True
if self.key is None:
return True
# 得到了key,就可以拿到这个ip访问此网站的 时间列表
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):
"""
Returns the recommended next request time in seconds.
"""
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)
内置类的使用
定义内置类,在类中为rate属性赋值不推荐,或者为scope属性赋值,配合setting使用
在自定义的类中: rate = '3/m' 就像权限中的可以定义的字段message一样
- 比较简单的,但是不利于更改,程序大了要找到这个并不容易
scope = 'MyThrottle' # 配合全局Settings中使用,可随时更改,方便查找,名字自取
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES':{
'MyThrottle':'3/m'
# 格式scope:rate
# rate格式 次数 / 规定时间
},
}
- 这个配置是为类的编写而定义的。
以上二者选其一,
上面的都自定义了限流类,重写rate或scope+setting
,为rate赋值是将这个限流类规定为固定rate的限流类,而为scope赋值赋的值一般是限流类的名字,反正是起个名字,不在类中指定具体的rate,在使用时,按照上面代码格式赋值。
若要继承SimpleRateThrottle类,就必须重写get_cache_key()返回一个唯一标识。
class MyThrottle(SimpleRateThrottle):
rate = '3/m' # 比较简单的,但是不利于更改,程序大了要找到这个并不容易
scope = 'MyThrottle' # 配合全局Settings中使用,可随时更改,方便查找
# 以上二者选其一
# 重写get_cache_key方法,设置一个唯一标志的key。
def get_cache_key(self, request, view):
return request.META.get('REMOTE_ADDR')
定义限流类时,局部+全局
- rate = 3/m
- scope = 'Name',settings中,
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES':{
'MyThrottle':'3/m'
},
}
当使用节流类的时候,还是哪两种方法,全局 + 局部
'DEFAULT_THROTTLE_CLASSES' : '节流类路径'
和throttle_classes = [节流类]
这些类不能放在views中,所以可以新建一个package,里面放throttle.py,permission.py,authentic.py文件。
问题:
初始化视图类的时候同样是来到了perform_authentication,check_permission,check_throttle,为什么认证时返回的是request.user
当用户登录网站后,用户的认证往往执行一次就够了,后续不会再次执行,所以在执行perform_authentication时不能直接执行认证类实例.认证方法。取而代之的是通过纽带request来实现,将request.user是一个方法,他hasattr找request中是否有_user信息,有说明认证过,直接返回request._user,没有说明未认证,转到一个函数去执行认证实例的认证方法。认证是分开的,所以在request中有authenticators,而没有throttles,permissions,在执行认证类实例的认证方法时for遍历的就是authenticators,而权限和节流中执行的是self.get_permissons(),slef.get_throttles()。