从最开始的DRF知识介绍到现在,我们也知道频率组件是在APIView类源码中的dispatch方法里面执行了三大认证,这三大认证也是在视图类方法之前就执行了。
'这里我就不一个个罗列出来了,就直接写有关频率源码了。'
def dispatch(self, request, *args, **kwargs):
try:
'执行三大认证'
self.initial(request, *args, **kwargs)
'APIView里面的initial方法'
def initial(self, request, *args, **kwargs):
'执行频率'
self.check_throttles(request)
'APIView里面的check_throttles方法'
def check_throttles(self, request):
throttle_durations = []
'这里的self.get_throttles方法,就是获取视图类中一个个的频率类的对象,它是一个列表'
'然后循环遍历,把一个个的频率类的对象给了throttle'
for throttle in self.get_throttles():
'这里就是为什么我们继承BaseThrottle需要重写这个方法的原因。'
if not throttle.allow_request(request, self):
'''
当这个方法的返回值为False时,则获取等待时间并添加到throttle_durations这个列表中
这里的throttle.wait就是重写的wait方法的返回值,返回的还剩多长时间才能进行下一次的访问
'''
throttle_durations.append(throttle.wait())
'如果执行了频率限制,就会执行这里'
if throttle_durations:
'这里的列表表达式就是duration在这个throttle_durations列表中有还剩多长时间的数据'
durations = [
duration for duration in throttle_durations
if duration is not None # 这里它过滤掉了为空的
]
'然后在这里取出了这个列表中的最大值,例子:duration[33,54]'
duration = max(durations, default=None) # 这里就会取出最大的那个54
'这里的self就是视图类的对象,所以我们在APIView中找到了throttled方法'
self.throttled(request, duration)
'duration有两种情况,一种是有值(被限制频率),一种是None(没有被限制频率)'
'APIView中的throttled方法'
'我们可以看到在上面它把最后的duration传入到这个throttled方法中了'
def throttled(self, request, wait):
'所以这里的wait就是最后的druation的值,所以这里的wait也是有两种情况,有值或者None'
raise exceptions.Throttled(wait)
'''
这里的exceptions.Throttled()就是from rest_framework.exceptions import Throttled
也就是类实例化得到对象后把wait也就是duration里面的数字或者None传入进去,
具体这个exceptions.Throttled方法里面是如果把我们限制的数字拼接到页面上的这里就不多介绍
'''
我们从上面
APIView
中的频率源码知道,继承SimpleRateThrottle频率类
和继承BaseThrottle频率类
,本质都是差不多,都是重写一些方法,而SimpleRateThrottle频率类
,它的源码中就是继承了BaseThrottle
频率类,所以这里我就直接看SimpleRateThrottle
的源码
'我们自己写自定义频率类的时候,继承SimpleRateThrottle频率类,发现并没有重写allow_request方法,而是写的别的'
'''
所以我们在这里去SimpleRateThrottle源码中看看它是如何实现的,
在SimpleRateThrottle源码中我们看到了它重写了allow_request方法,并且它就是继承了BaseThrottle类
'''
class SimpleRateThrottle(BaseThrottle):
def allow_request(self, request, view):
"""
参数:
- request: 当前的请求对象
- view: 请求对应的视图对象或处理函数
返回:
- True: 允许请求通过
- False: 请求被限制
注意: 这里的逻辑假设限流器的主要参数在初始化时已经设置,
如速率(rate)、缓存键(key)、历史记录(history)等。
"""
'我们自己自定义的时候写了这个rate,例如:rate=5/m'
if self.rate is None: # 当rate为空执行,直接没有限制
return True
'这里调用了get_chche_key(),这就是为什么我们自定义频率类重写的是这个方法'
'所以这里的self.key就是重写的get_chche_key方法的返回值,例如按照ip限制,那就是ip'
self.key = self.get_cache_key(request, view)
'这里self.key也就是get_chche_key方法的返回值是空的时候执行,相当于没有进行限制'
if self.key is None:
return True
'''
self.key就是get_chche_key方法的返回的限制
self.cache.get就是缓存,默认设置缓存在内存中,然后去缓存中根据self.key的值,去取出访问者时间列表
如果没有则是[]
self.history就是当前ip,访问的时间列表
'''
self.history = self.cache.get(self.key, [])
'这里的self.timer就是加括号调用time.time实例化对象self.now,拿到当前执行时间'
self.now = self.timer() # timer = time.time没有加括号
'''
这里就是拿到访问列表的最后一个时间数据(也是最早的时间数据,这个我们继承BaseThrottle写自定义频率类的时候做过这个类似的逻辑)
然后当它小于或者等于,当前时间减去self.duration也就是对应的60秒,当它超过了60秒就给超出的数据弹出
'''
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
'''
这里跟我们继承BaseThrottle写自定义频率类时类似,就是判断这个列表中的时间数据有几个,
当它大于我们我们设置的rate=5/m中的5个就执行
'''
if len(self.history) >= self.num_requests:
'这里的self.throttle_failure方法也是SimpleRateThrottle源码内的方法,它直接返回了False'
return self.throttle_failure()
'这里我们就可以知道,可以在自定义频率类中重写这个self.throttle_failure方法,定制返回的文字'
'''
这个selfthrottle_success方法跟上面一样,它直接返回True,
def throttle_success(self):
它把没有被限制的时间一个个放到history这个列表的第零个位置,这也就是我上面说的history[-1]是最早的时间
self.history.insert(0, self.now)
然后在这里又放入到缓存当中
self.cache.set(self.key, self.history, self.duration)
return True
'''
return self.throttle_success()
'在上面我并没有去说self.duration和self.num_requests到底为什么是rate=5/m中的60和5。我在这里说一下'
'首先直接按住Ctrl加鼠标左键点击self.duration就跳转到了它的初始化方法了'
def __init__(self):
'这里可以看到self.duration和self.num_requests是self.parse_rate解压赋值的,所以我们去看看这个方法'
self.num_requests, self.duration = self.parse_rate(self.rate)
'这个parse_rate也是SimpleRateThrottle的方法'
def parse_rate(self, rate):
'当rate为空时,设置两个空'
if rate is None:
return (None, None)
'当有值时按/进行切分,然后解压赋值给num、period两'
num, period = rate.split('/') # 例子5/m 所以这里的num就是5,period就是m
'然后再把/斜杠前的数据强转成数字类型后赋值给num-requests'
num_requests = int(num) # 到了这一步我们就知道,为什么num_requests就是rate=5/m的5了
'''
这里可以看到duration就是根据period这个第一个字母(索引)的key去duration这个字典中取值,这里都是设置好的
就是时分秒对应的秒数,这也是为什么我上面的duration说是60了因为对应的rate=5/m中的m就是minute分钟
它这里的写法比较高级,直接取第一个字符,不管你后面写了什么只要对应上第一个字符就可以
'''
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
'最后给这两个值返回出去'
return (num_requests, duration)