Django Rest Framework源码剖析(三)-----频率控制
<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0"> |
启用频率,DRF内部会有一个字典记录来访者的IP,以及访问时间最近几(通过配置)次的访问时间,这样确保每次列表中最后一个元素都是该用户请求的最早时间,形式如下:
举例说明,比如我现在配置了5秒内只能访问2次,每次请求到达频率控制时候先判断请求者IP是否已经在这个请求字典中,若存在,在判断用户请求5秒内的请求次数,若次数小于等于2,则允许请求,若大于2,则超过频率,不允许请求。
关于请求频率的的算法(以5秒内最多访问两次为例):
1.首先删除掉列表里5秒之前的请求,循环判断当前请求时间和最早请求时间之差记作t1,若t1大于5则代表列表中最早的请求已经在5秒外了,删除掉,继续判断倒数第二个请求,直到t1小于5.
2.当确保请求列表中只有5秒内请求时候,接着判断其请求次数(列表长度),若长度大于2,则证明超过5秒内访问超过2次了,则不允许,否则,通过并将此次访问时间插入到列表最前面,作为最新访问时间。
1.在utils目录下新建立文件,throttle.py,添加频率控制为每分钟只能访问5次
<span style="color: #800000;">"""<span style="color: #800000;">5秒内最多访问三次<span style="color: #800000;">"""<span style="color: #000000;">
scope = <span style="color: #800000;">"<span style="color: #800000;">WD<span style="color: #800000;">" <span style="color: #008000;">#<span style="color: #008000;">settings配置文件中的key,用于获取配置的频率
<span style="color: #0000ff;">def</span><span style="color: #000000;"> get_cache_key(self,request,view):
</span><span style="color: #0000ff;">return</span> self.get_ident(request)</pre>
2.settings.py中配置全局频率控制
urls.py
url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/auth</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.AuthView.as_view()),url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/order</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.OrderView.as_view()),]</span></pre>
models.py
user_type_choice =<span style="color: #000000;"> (
(1,<span style="color: #800000;">"<span style="color: #800000;">普通用户<span style="color: #800000;">"<span style="color: #000000;">),(2,<span style="color: #800000;">"<span style="color: #800000;">会员<span style="color: #800000;">"<span style="color: #000000;">),)
user_type = models.IntegerField(choices=<span style="color: #000000;">user_type_choice)
username = models.CharField(max_length=32,unique=<span style="color: #000000;">True)
password = models.CharField(max_length=64<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> UserToken(models.Model):
user = models.OneToOneField(to=<span style="color: #000000;">UserInfo)
token = models.CharField(max_length=64)
订单视图
使用postman验证如下图,可以看到频率限制已经起作用了。
dispatch()
<span style="color: #0000ff;">try</span><span style="color: #000000;">:
self.initial(request,</span>*args,**<span style="color: #000000;">kwargs)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Get the appropriate handler method</span>
<span style="color: #0000ff;">if</span> request.method.lower() <span style="color: #0000ff;">in</span><span style="color: #000000;"> self.http_method_names:
handler </span>=<span style="color: #000000;"> getattr(self,request.method.lower(),self.http_method_not_allowed)
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
handler </span>=<span style="color: #000000;"> self.http_method_not_allowed
response </span>= handler(request,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> Exception as exc:
response </span>=<span style="color: #000000;"> self.handle_exception(exc)
self.response </span>= self.finalize_response(request,response,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">return</span> self.response</pre>
2.执行inital方法,initial方法中执行check_throttles则开始频率控制
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Perform content negotiation and store the accepted info on the request</span>
neg =<span style="color: #000000;"> self.perform_content_negotiation(request)
request.accepted_renderer,request.accepted_media_type </span>=<span style="color: #000000;"> neg
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Determine the API version,if versioning is in use.</span>
version,scheme = self.determine_version(request,**<span style="color: #000000;">kwargs)
request.version,request.versioning_scheme </span>=<span style="color: #000000;"> version,scheme
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Ensure that the incoming request is permitted</span>
<span style="color: #008000;">#</span><span style="color: #008000;">2.实现认证</span>
<span style="color: #000000;"> self.perform_authentication(request)
<span style="color: #000000;"> self.check_permissions(request)
<span style="color: #008000;">#<span style="color: #008000;">4.频率限制
self.check_throttles(request)
3.下面是check_throttles源码,与认证、权限一样采用列表对象方式,通过判断allow_request方法返回值判断频率是否通过
4.get_throttles方法,采用列表生成式生成频率控制对象,与认证、权限一直
5.self.throttle_classes属性获取
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The following policies may be set at either globally,or per-view.</span>
renderer_classes =<span style="color: #000000;"> api_settings.DEFAULT_RENDERER_CLASSES
parser_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_PARSER_CLASSES
authentication_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_AUTHENTICATION_CLASSES
<span style="color: #ff6600;"> throttle_classes
<span style="color: #ff6600;">=<span style="color: #000000;"><span style="color: #ff6600;"> api_settings.DEFAULT_THROTTLE_CLASSES #频率控制全局配置permission_classes =<span style="color: #000000;"> api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class =<span style="color: #000000;"> api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class =<span style="color: #000000;"> api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
6.通过以上分析,知道了频率控制是通过判断每个类中的allow_request放法的返回值来判断频率是否通过,下面我们来看看我们所使用的SimpleRateThrottle怎么实现的,分析部分请看注解:
SimpleRateThrottle类源码:
The rate (requests / seconds) is set by a `rate` attribute on the View
class. The attribute is a string of the form 'number_of_requests/period'.
Period should be one of: ('s','sec','m','min','h','hour','d','day')
Previous request information used for throttling is stored in the cache.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
cache </span>=<span style="color: #000000;"> default_cache <span style="color: #ff9900;"># 存放请求时间,类似与示例中的大字典,这里使用的是django的缓存</span>
timer </span>=<span style="color: #000000;"> time.time
cache_format </span>= <span style="color: #800000;">'</span><span style="color: #800000;">throttle_%(scope)s_%(ident)s</span><span style="color: #800000;">'</span><span style="color: #000000;">
scope </span>=<span style="color: #000000;"> None
THROTTLE_RATES </span>=<span style="color: #000000;"> api_settings.DEFAULT_THROTTLE_RATES
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> getattr(self,<span style="color: #800000;">'</span><span style="color: #800000;">rate</span><span style="color: #800000;">'</span><span style="color: #000000;">,None):
self.rate </span>=<span style="color: #000000;"> self.get_rate()
self.num_requests,self.duration </span>=<span style="color: #000000;"> self.parse_rate(self.rate)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_cache_key(self,view):<span style="color: #ff9900;"> # 获取请求的key标识,必须要有否则会报错,这里可以重写,使用用户的用户名、或其他作为key,在示例中使用的get_ident方法用户获取用户IP作为key
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Should return a unique cache-key which can be used for throttling.
Must be overridden.
May return `None` if the request should not be throttled.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">raise</span> NotImplementedError(<span style="color: #800000;">'</span><span style="color: #800000;">.get_cache_key() must be overridden</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_rate(self): <span style="color: #ff9900;"># 获取配置文件的配置速率
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Determine the string representation of the allowed request rate.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> getattr(self,<span style="color: #800000;">'</span><span style="color: #800000;">scope</span><span style="color: #800000;">'</span><span style="color: #000000;">,None): <span style="color: #ff9900;"># 通过获取共有属性scope来获取配置的速率</span>
msg </span>= (<span style="color: #800000;">"</span><span style="color: #800000;">You must set either `.scope` or `.rate` for '%s' throttle</span><span style="color: #800000;">"</span> %<span style="color: #000000;">
self.</span><span style="color: #800080;">__class__</span>.<span style="color: #800080;">__name__</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> ImproperlyConfigured(msg)
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.THROTTLE_RATES[self.scope]
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> KeyError:
msg </span>= <span style="color: #800000;">"</span><span style="color: #800000;">No default throttle rate set for '%s' scope</span><span style="color: #800000;">"</span> %<span style="color: #000000;"> self.scope
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> ImproperlyConfigured(msg)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> parse_rate(self,rate): <span style="color: #ff9900;"># 格式化速率
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Given the request rate string,return a two tuple of:
<allowed number of requests>,<period of time in seconds>
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span> rate <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (None,None)
num,period </span>= rate.split(<span style="color: #800000;">'</span><span style="color: #800000;">/</span><span style="color: #800000;">'</span><span style="color: #000000;">) <span style="color: #ff9900;"># 分离字符串</span>
num_requests </span>=<span style="color: #000000;"> int(num)
duration </span>= {<span style="color: #800000;">'</span><span style="color: #800000;">s</span><span style="color: #800000;">'</span>: 1,<span style="color: #800000;">'</span><span style="color: #800000;">m</span><span style="color: #800000;">'</span>: 60,<span style="color: #800000;">'</span><span style="color: #800000;">h</span><span style="color: #800000;">'</span>: 3600,<span style="color: #800000;">'</span><span style="color: #800000;">d</span><span style="color: #800000;">'</span>: 86400<span style="color: #000000;">}[period[0]] <span style="color: #ff9900;"># 转换时间为数字,示例配置的5/m,m转为60秒
</span></span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (num_requests,duration)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> allow_request(self,view): <span style="color: #ff9900;"> # 判断请求的速率是否通过
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Implement the check to see if the request should be throttled.
On success calls `throttle_success`.
On failure calls `throttle_failure`.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span> self.rate <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
self.key </span>=<span style="color: #000000;"> self.get_cache_key(request,view)
</span><span style="color: #0000ff;">if</span> self.key <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
self.history </span>=<span style="color: #000000;"> self.cache.get(self.key,[])
self.now </span>=<span style="color: #000000;"> self.timer()
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Drop any requests from the history which have now passed the</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> throttle duration</span>
<span style="color: #0000ff;">while</span> self.history <span style="color: #0000ff;">and</span> self.history[-1] <= self.now -<span style="color: #000000;"> self.duration: <span style="color: #ff9900;"># 频率判断实现原理,已经举例进行了说明</span>
self.history.pop()
</span><span style="color: #0000ff;">if</span> len(self.history) >=<span style="color: #000000;"> self.num_requests:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.throttle_failure()
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.throttle_success()
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> throttle_success(self): <span style="color: #ff9900;"># 频率通过返回true
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Inserts the current request's timestamp along with the key
into the cache.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
self.history.insert(0,self.now)
self.cache.set(self.key,self.history,self.duration)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> throttle_failure(self): <span style="color: #ff9900;"># 不通过返回false
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Called when a request to the API has failed due to throttling.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> False
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> wait(self): <span style="color: #ff9900;"> # 返回等待时间
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Returns the recommended next request time in seconds.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> self.history:
remaining_duration </span>= self.duration - (self.now - self.history[-1<span style="color: #000000;">])
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
remaining_duration </span>=<span style="color: #000000;"> self.duration
available_requests </span>= self.num_requests - len(self.history) + 1
<span style="color: #0000ff;">if</span> available_requests <=<span style="color: #000000;"> 0:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
</span><span style="color: #0000ff;">return</span> remaining_duration / float(available_requests)</pre>
get_ident方法源码,该方法用于获取请求的IP:
</span><span style="color: #0000ff;">if</span> num_proxies <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">if</span> num_proxies == 0 <span style="color: #0000ff;">or</span> xff <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> remote_addr
addrs </span>= xff.split(<span style="color: #800000;">'</span><span style="color: #800000;">,</span><span style="color: #800000;">'</span><span style="color: #000000;">)
client_addr </span>= addrs[-<span style="color: #000000;">min(num_proxies,len(addrs))]
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> client_addr.strip()
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">''</span>.join(xff.split()) <span style="color: #0000ff;">if</span> xff <span style="color: #0000ff;">else</span> remote_addr</pre>
1.BaseThrottle:最基本的频率控制需要重写allow_request方法和wait方法
<span style="color: #0000ff;">def</span><span style="color: #000000;"> allow_request(self,view):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Return `True` if the request should be allowed,`False` otherwise.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">raise</span> NotImplementedError(<span style="color: #800000;">'</span><span style="color: #800000;">.allow_request() must be overridden</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_ident(self,if not use REMOTE_ADDR.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
xff </span>= request.META.get(<span style="color: #800000;">'</span><span style="color: #800000;">HTTP_X_FORWARDED_FOR</span><span style="color: #800000;">'</span><span style="color: #000000;">)
remote_addr </span>= request.META.get(<span style="color: #800000;">'</span><span style="color: #800000;">REMOTE_ADDR</span><span style="color: #800000;">'</span><span style="color: #000000;">)
num_proxies </span>=<span style="color: #000000;"> api_settings.NUM_PROXIES
</span><span style="color: #0000ff;">if</span> num_proxies <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">if</span> num_proxies == 0 <span style="color: #0000ff;">or</span> xff <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> remote_addr
addrs </span>= xff.split(<span style="color: #800000;">'</span><span style="color: #800000;">,len(addrs))]
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> client_addr.strip()
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">''</span>.join(xff.split()) <span style="color: #0000ff;">if</span> xff <span style="color: #0000ff;">else</span><span style="color: #000000;"> remote_addr
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> wait(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Optionally,return a recommended number of seconds to wait before
the next request.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span> None</pre>
2.SimpleRateThrottle:示例中已经使用,并对源码和原理进行了分析。
Previous request information used for throttling is stored in the cache.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
cache </span>=<span style="color: #000000;"> default_cache
timer </span>=<span style="color: #000000;"> time.time
cache_format </span>= <span style="color: #800000;">'</span><span style="color: #800000;">throttle_%(scope)s_%(ident)s</span><span style="color: #800000;">'</span><span style="color: #000000;">
scope </span>=<span style="color: #000000;"> None
THROTTLE_RATES </span>=<span style="color: #000000;"> api_settings.DEFAULT_THROTTLE_RATES
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> getattr(self,view):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Should return a unique cache-key which can be used for throttling.
Must be overridden.
May return `None` if the request should not be throttled.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">raise</span> NotImplementedError(<span style="color: #800000;">'</span><span style="color: #800000;">.get_cache_key() must be overridden</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_rate(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Determine the string representation of the allowed request rate.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> getattr(self,None):
msg </span>= (<span style="color: #800000;">"</span><span style="color: #800000;">You must set either `.scope` or `.rate` for '%s' throttle</span><span style="color: #800000;">"</span> %<span style="color: #000000;">
self.</span><span style="color: #800080;">__class__</span>.<span style="color: #800080;">__name__</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> ImproperlyConfigured(msg)
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.THROTTLE_RATES[self.scope]
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> KeyError:
msg </span>= <span style="color: #800000;">"</span><span style="color: #800000;">No default throttle rate set for '%s' scope</span><span style="color: #800000;">"</span> %<span style="color: #000000;"> self.scope
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> ImproperlyConfigured(msg)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> parse_rate(self,rate):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Given the request rate string,period </span>= rate.split(<span style="color: #800000;">'</span><span style="color: #800000;">/</span><span style="color: #800000;">'</span><span style="color: #000000;">)
num_requests </span>=<span style="color: #000000;"> int(num)
duration </span>= {<span style="color: #800000;">'</span><span style="color: #800000;">s</span><span style="color: #800000;">'</span>: 1,<span style="color: #800000;">'</span><span style="color: #800000;">d</span><span style="color: #800000;">'</span>: 86400<span style="color: #000000;">}[period[0]]
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (num_requests,view):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Implement the check to see if the request should be throttled.
On success calls `throttle_success`.
On failure calls `throttle_failure`.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span> self.rate <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
self.key </span>=<span style="color: #000000;"> self.get_cache_key(request,[])
self.now </span>=<span style="color: #000000;"> self.timer()
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Drop any requests from the history which have now passed the</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> throttle duration</span>
<span style="color: #0000ff;">while</span> self.history <span style="color: #0000ff;">and</span> self.history[-1] <= self.now -<span style="color: #000000;"> self.duration:
self.history.pop()
</span><span style="color: #0000ff;">if</span> len(self.history) >=<span style="color: #000000;"> self.num_requests:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.throttle_failure()
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.throttle_success()
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> throttle_success(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Inserts the current request's timestamp along with the key
into the cache.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
self.history.insert(0,self.duration)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> throttle_failure(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Called when a request to the API has failed due to throttling.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> False
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> wait(self):
</span><span style="color: #800000;">"""</span><span style="color: #800000;">
Returns the recommended next request time in seconds.
</span><span style="color: #800000;">"""</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> self.history:
remaining_duration </span>= self.duration - (self.now - self.history[-1<span style="color: #000000;">])
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
remaining_duration </span>=<span style="color: #000000;"> self.duration
available_requests </span>= self.num_requests - len(self.history) + 1
<span style="color: #0000ff;">if</span> available_requests <=<span style="color: #000000;"> 0:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
</span><span style="color: #0000ff;">return</span> remaining_duration / float(available_requests)</pre>
3.AnonRateThrottle:匿名用户频率控制
The IP address of the request will be used as the unique cache key.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
scope </span>= <span style="color: #800000;">'</span><span style="color: #800000;">anon</span><span style="color: #800000;">'</span>
<span style="color: #0000ff;">def</span><span style="color: #000000;"> get_cache_key(self,view):
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> request.user.is_authenticated:
</span><span style="color: #0000ff;">return</span> None <span style="color: #008000;">#</span><span style="color: #008000;"> Only throttle unauthenticated requests.</span>
<span style="color: #0000ff;">return</span> self.cache_format %<span style="color: #000000;"> {
</span><span style="color: #800000;">'</span><span style="color: #800000;">scope</span><span style="color: #800000;">'</span><span style="color: #000000;">: self.scope,</span><span style="color: #800000;">'</span><span style="color: #800000;">ident</span><span style="color: #800000;">'</span><span style="color: #000000;">: self.get_ident(request)
}</span></pre>
4.UserRateThrottle:基于SimpleRateThrottle,对用户的频率控制
The user id will be used as a unique cache key if the user is
authenticated. For anonymous requests,the IP address of the request will
be used.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
scope </span>= <span style="color: #800000;">'</span><span style="color: #800000;">user</span><span style="color: #800000;">'</span>
<span style="color: #0000ff;">def</span><span style="color: #000000;"> get_cache_key(self,view):
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> request.user.is_authenticated:
ident </span>=<span style="color: #000000;"> request.user.pk
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
ident </span>=<span style="color: #000000;"> self.get_ident(request)
</span><span style="color: #0000ff;">return</span> self.cache_format %<span style="color: #000000;"> {
</span><span style="color: #800000;">'</span><span style="color: #800000;">scope</span><span style="color: #800000;">'</span><span style="color: #000000;">: self.scope,</span><span style="color: #800000;">'</span><span style="color: #800000;">ident</span><span style="color: #800000;">'</span><span style="color: #000000;">: ident
}</span></pre>
<span style="color: #0000ff;">class
<span style="color: #000000;"> VisitThrottle(BaseThrottle):<span style="color: #800000;">'''<span style="color: #800000;">60s内最多能访问5次<span style="color: #800000;">'''
<span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self):
self.history </span>=<span style="color: #000000;"> None
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> allow_request(self,view):
</span><span style="color: #008000;">#</span><span style="color: #008000;"> 获取用户ip (get_ident)</span>
remote_addr =<span style="color: #000000;"> self.get_ident(request)
ctime </span>=<span style="color: #000000;"> time.time()
</span><span style="color: #0000ff;">if</span> remote_addr <span style="color: #0000ff;">not</span> <span style="color: #0000ff;">in</span><span style="color: #000000;"> REQUEST_RECORD:
REQUEST_RECORD[remote_addr] </span>= [ctime,] <span style="color: #008000;">#</span><span style="color: #008000;"> 保持请求的时间,形式{ip:[时间,]}</span>
<span style="color: #0000ff;">return</span> True <span style="color: #008000;">#</span><span style="color: #008000;"> True表示可以访问</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 获取当前ip的历史访问记录</span>
history =<span style="color: #000000;"> REQUEST_RECORD.get(remote_addr)
self.history </span>=<span style="color: #000000;"> history
</span><span style="color: #0000ff;">while</span> history <span style="color: #0000ff;">and</span> history[-1] < ctime - 60<span style="color: #000000;">:
</span><span style="color: #008000;">#</span><span style="color: #008000;"> while循环确保每列表中是最新的60秒内的请求</span>
<span style="color: #000000;">
history.pop()
<span style="color: #008000;">#<span style="color: #008000;"> 访问记录小于5次,将本次请求插入到最前面,作为最新的请求
<span style="color: #0000ff;">if len(history) < 5<span style="color: #000000;">:
history.insert(0,ctime)
<span style="color: #0000ff;">return<span style="color: #000000;"> True
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> wait(self):
</span><span style="color: #800000;">'''</span><span style="color: #800000;">返回等待时间</span><span style="color: #800000;">'''</span><span style="color: #000000;">
ctime </span>=<span style="color: #000000;"> time.time()
</span><span style="color: #0000ff;">return</span> 60 - (ctime - self.history[-1])</pre>
- 继承BaseThrottle类
- 重写request_allow方法和wait方法,request_allow方法返回true代表通过,否则拒绝,wait返回等待的时间
2.配置
throttle_classes =<span style="color: #000000;"> [VisitThrottle,] <span style="color: #008000;">#<span style="color: #008000;">#优先级
单一视图>全局
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!