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源码之频率控制剖析的更多相关文章

  1. 五、django rest_framework源码之版本控制剖析

    1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...

  2. 一、django rest_framework源码之总体流程剖析

    1 序言 有如下django代码,视图层: from django.http import HttpResponse from rest_framework.views import APIView ...

  3. 二、django rest_framework源码之认证流程剖析

    1 绪言 上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法.在dispatch方法中,在调用了一个initial方法,所有的认证.权限 ...

  4. 六、django rest_framework源码之解析器剖析

    1 绪论 网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framewor ...

  5. 三、django rest_framework源码之权限流程剖析

    1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...

  6. 七、django rest_framework源码之视图

    1 绪言 当大家看大这篇博文的时候,应该对Django rest_framework中的CBV有所了解了,大致来说就是通过定义类来继承APIView类,并在类中定义get.post.put.delet ...

  7. Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

    目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...

  8. java 日志体系(四)log4j 源码分析

    java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...

  9. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

随机推荐

  1. An Insight to References in C++

    [An Insight to References in C++] 引用的本质是常指针.占用的内存和指针一样. 参考:http://www.codeproject.com/Articles/13363 ...

  2. windows git gui右键sublime打开当前文件编辑

    git安装目录\Git\libexec\git-core\git-gui.tcl的 proc create_common_diff_popup 下追加: $ctxm add command \ -la ...

  3. c++程序设计中的函数重载

    函数重载的意思是在一个作用域内(命名空间内)定义了某个或某些具有相同名称的函数,但是他们的参数列表和定义(实现)不相同,如果相同的话,就没啥意义了.当调用一个重载函数时,编译器会通过所使用的参数类型. ...

  4. Oracle分析函数Over()

    一.Over()分析函数 说明:聚合函数(如sum().max()等)可以计算基于组的某种聚合值,但是聚合函数对于某个组只能返回一行记录.若想对于某组返回多行记录,则需要使用分析函数. 1.rank( ...

  5. HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分

    这是系列第二部分,之前部分在本博客中找 源码demo存放在https://github.com/willian12345/Box2D-for-Javascript-Games 向世界添加刚体 刚体(B ...

  6. 最短路径之迪杰斯特拉(Dijkstra)算法

    对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点为源点,最后一个顶点为终点.最短路径的算法主要有迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd ...

  7. IDL界面程序直接调用envi菜单对应功能

    参考自http://blog.sina.com.cn/s/blog_764b1e9d010115qu.html 参考文章的方法是构建一个button控件,通过单击实现,这种方法比较复杂,不是我们经常能 ...

  8. HDU 6196 happy happy happy 爆搜加剪枝

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6196 题意:给你长度为n的序列,爸爸和儿子玩一个游戏,儿子先手,儿子每次都选择最左边与最右边最大的那个 ...

  9. Scala中“=>”用法及含义

    => has several meanings in Scala, all related to its mathematical meaning as implication. 1. In a ...

  10. Hadoop(二):MapReduce程序(Java)

    Java版本程序开发过程主要包含三个步骤,一是map.reduce程序开发:第二是将程序编译成JAR包:第三使用Hadoop jar命令进行任务提交. 下面拿一个具体的例子进行说明,一个简单的词频统计 ...