四、django rest_framework源码之频率控制剖析
1 绪言
权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码。
2 源码分析
访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始。入口如下:
def check_throttles(self, request):
for throttle in self.get_throttles():#遍历每一个频率控制对象
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())#wait方法返回还需要等待多少秒才可以访问
get_throttles是获取所有的频率控制类的实例对象,源码如下:
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
获取和实例化的方法都是通过列表生成式和读取配置的频率控制类,与认证、权限如出一辙,这里不再赘述。关键过程在执行实例化对象里的方法,这里以rest_framework自带的SimpleRateThrottle类为例进行分析。check_throttles方法内的for循环开始后,首先获取一个频率控制实例,然后执行allow_request方法:
def allow_request(self, request, view):
if self.rate is None:#如果配置中设置的频率是None,就是不限制访问频率,直接返回True
return True
#get_cache_key的作用是从request中获取访问端标识(例如用户名、IP)
#这个方法必须被之类覆写
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
#下面的cache是django自带的缓存
#从缓存中取出访问记录(一个列表),如果找不到(没有访问过)就赋值为一个空列表
self.history = self.cache.get(self.key, [])
self.now = self.timer()#获取当前时间
#如果有访问记录,先删除在访问时间段之外的记录
# 以3/m为例,时间段为1分钟,那么就是删除一分钟以前的记录
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
#如果剩余的访问记录数量多于访问最大频次(前一分钟访问次数超过3次)
if len(self.history) >= self.num_requests:
return self.throttle_failure()#不能再访问了
return self.throttle_success()#继续访问吧
allow_request方法内的self.rate属性是在构造方法中设置的,构造方法如下:
def __init__(self):
if not getattr(self, 'rate', None):#如果没有rate属性
self.rate = self.get_rate()#获得配置好的频率,形如:'3/m'
#对频率(例如’3/m')进行解析,分割成频次3,和时间间隔m
self.num_requests, self.duration = self.parse_rate(self.rate)
get_rate方法:
def get_rate(self):
if not getattr(self, 'scope', None):#如果没有设置scope属性就抛出异常
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:#如果设置了scope,就去配置中通过scope取出这个配置
#THROTTLE_RATES是在settings.py中的频率控制配置项,是一个字典
return self.THROTTLE_RATES[self.scope]#返回配置好的频率,形如:'3/m'
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
parse_rate方法:
def parse_rate(self, rate):
if rate is None:
return (None, None)
# 配置中设置的频率格式为:’3/m'
num, period = rate.split('/')
num_requests = int(num)
#获取时间间隔,s为秒,m为分
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration)#返回一个tuple
那么,此时allow_request的第一行代码就成功获得了配置好的频率。之所以要把get_rate和parse_rate方法源码贴出来,是因为方法里面出现了scope属性,这个属性配置用户配置我们的频率,例如我们要配置一分钟访问三次,则在我们自定义的类中首先需要给scope一个字符串值,例如scope=“xxx” , 然后在settings.py中进行如下配置:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'xxx': '3/m',
},
}
另外,allow_request中调用了一个get_cache_key方法,该方法的作用是获取访问端的标识,这个方法必须被覆写,否则会抛出异常。
继续贴出接受访问和拒绝访问时执行的方法源码:
def throttle_success(self):
# 如果可以访问,就将当前时间加入到缓存中
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True#返回True标识可以访问
def throttle_failure(self):
return False#返回False表示拒绝访问
至于在allow_request方法中如何进行访问判断,在代码中有详细注释。
在退出allow_request方法后,如果被拒绝,最初被执行的check_throttles方法会调用一个wait方法,这个方法返回的是还有多少秒可以访问。
3 自定义频率控制类
方法一:完全自己重新写一个频率控制类
import time
VISIT_RECORD = {} #存放IP的数据库 可以放在缓存!
class VisitThrattle(object):
def __init__(self):
self.history = None
def allow_request(self, request, view):
remote_addr = request._request.META.get('REMOTE_ADDR')#获取IP
ctime = time.time()#当前时间
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,] #表示第一次访问
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[[-1] < ctime -60:
history.pop()
if len(history) < 3:
history.insert(0, ctime)
return True
return False
方法二:继承django rest_framework中的类
根据IP进行频率控制:
class VisitThrottle(SimpleRateThrottle): #对匿名用户的ip号通过时间做访问频率控制
scope = 'IPScope'
def get_cache_key(self, request, view): #去缓存里取数据
return self.get_ident(request)#这是BaseThrottle中的方法
根据用户名进行频率控制:
class UserThrottle(SimpleRateThrottle): #对用户的名字 通过时间做访问频率控制
scope = "userScope"
def get_cache_key(self, request, view):
return request.user.username
4 频率控制配置
在settings.py文件中配置DEFAULT_THROTTLE_RATES,DEFAULT_THROTTLE_RATES里面的键必须与频率控制类里面的scope的值一一对应。:
REST_FRAMEWORK = {
……
'DEFAULT_THROTTLE_RATES': {
'IPScope': '3/minute',
'userScope': '3/minute'
}
}
然后配置DEFAULT_THROTTLE_CLASSES,有全局配置和局部配置之分,全局配置在settings.py文件中进行,局部配置在视图配种进行,配制方法与认证类、权限类的方法一直,这里不再介绍。
四、django rest_framework源码之频率控制剖析的更多相关文章
- 五、django rest_framework源码之版本控制剖析
1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...
- 一、django rest_framework源码之总体流程剖析
1 序言 有如下django代码,视图层: from django.http import HttpResponse from rest_framework.views import APIView ...
- 二、django rest_framework源码之认证流程剖析
1 绪言 上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法.在dispatch方法中,在调用了一个initial方法,所有的认证.权限 ...
- 六、django rest_framework源码之解析器剖析
1 绪论 网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framewor ...
- 三、django rest_framework源码之权限流程剖析
1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...
- 七、django rest_framework源码之视图
1 绪言 当大家看大这篇博文的时候,应该对Django rest_framework中的CBV有所了解了,大致来说就是通过定义类来继承APIView类,并在类中定义get.post.put.delet ...
- Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块
目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...
- java 日志体系(四)log4j 源码分析
java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...
- Django session 源码流程
流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...
随机推荐
- python 三种遍历列表里面序号和值的方法
list = ['html', 'js', 'css', 'python'] # 方法1 # 遍历列表方法1:' for i in list: print("序号:%s 值:%s" ...
- 悲催的IE6 七宗罪大吐槽(带解决方法)第三部分
五:文字溢出bug(注释bug) 1.在以下情况下将会引起文字溢出bug 一个容器包含2两个具有“float”样式的子容器. 第二个容器的宽度大于父容器的宽度,或者父容器宽度减去第二个容器宽度的值小于 ...
- 洛谷P3959 [NOIP2017]宝藏
[题目描述] 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋,也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但 ...
- Eclipse配置C++环境
由于实在不想用(界面太丑,超级强迫症),前段时间JAVA一直用eclipse,感觉这个IDE非常友好,看上去很舒服,下载的时候发现有C++版本,于是折腾了一会儿,谷歌上发现好多教程,但是大部分比较老, ...
- InnoDB 引擎独立表空间
InnoDB 引擎独立表空间 使用过MySQL的同学,刚开始接触最多的莫过于MyISAM表引擎了,这种引擎的数据库会分别创建三个文件:表结构.表索引.表数据空间.我们可以将某个数据库目录直接迁移到 ...
- 使用vue的一些经验
虽然说VUE是数据驱动视图的框架,但有时候不得不获取DOM来获得一些样式属性,做一些操作,这时候就需要VUE获取DOM对象的方法. vue获取DOM对象的方法: 如果是操作组件内部的DOM,可以通过给 ...
- 14 Go's Declaration Syntax go语言声明语法
Go's Declaration Syntax go语言声明语法 7 July 2010 Introduction Newcomers to Go wonder why the declaration ...
- Android 浏览器启动应用程序
点击浏览器中的URL链接,启动特定的App. 首先做成HTML的页面,页面内容格式如下: <a href="[scheme]://[host]/[path]?[query]" ...
- python网络编程-多进程multiprocessing
一:mutilprocess简介 多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去 ...
- JavaScript数据检测
前言: 随着编程实践的增加,慢慢发现关于数据类型的检测至关重要.我认为程序就是为了处理数据和展示数据.所以,数据的检测对于编程来说也至关重要.因为只有符合我们预期的输入,才可能产生正确的输出.众所周知 ...