4、django rest_framework源碼之頻率控制剖析

1 緒言

  權限斷定以後的下一個環節是訪問頻率控制,本篇咱們分析訪問頻率控制部分源碼。數據庫

2 源碼分析

        訪問頻率控制在dispatch方法中的initial方法調用check_throttles方法開始。入口以下:   django

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方法:ide

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方法:   spa

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方法:    rest

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中進行以下配置:code

REST_FRAMEWORK = {

    'DEFAULT_THROTTLE_RATES': {

        'xxx': '3/m',

    },
}

        另外,allow_request中調用了一個get_cache_key方法,該方法的做用是獲取訪問端的標識,這個方法必須被覆寫,不然會拋出異常。對象

繼續貼出接受訪問和拒絕訪問時執行的方法源碼:blog

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文件中進行,局部配置在視圖配種進行,配製方法與認證類、權限類的方法一直,這裏再也不介紹。

相關文章
相關標籤/搜索