权限断定以后的下一个环节是访问频率控制,本篇咱们分析访问频率控制部分源码。数据库
访问频率控制在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文件中进行,局部配置在视图配种进行,配制方法与认证类、权限类的方法一直,这里再也不介绍。