權限斷定以後的下一個環節是訪問頻率控制,本篇咱們分析訪問頻率控制部分源碼。數據庫
訪問頻率控制在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方法,這個方法返回的是還有多少秒能夠訪問。
方法一:徹底本身從新寫一個頻率控制類
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
在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文件中進行,局部配置在視圖配種進行,配製方法與認證類、權限類的方法一直,這裏再也不介紹。