加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Django Rest Framework源码剖析(三)-----频率控制

发布时间:2020-12-15 17:17:00 所属栏目:大数据 来源:网络整理
导读:table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0" tr td span style="font-size: 16px;"一、简介 /td /tr /table 承接上篇文章,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法
<tr>
<td><span style="font-size: 16px;">一、简介</td>
</tr></table>

承接上篇文章,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡、提高服务器配置、通过代理限制访问频率等,但是django rest framework自身就提供了访问频率的控制,可以从代码本身做控制。

<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0">

django rest framework 中频率控制基本原理基于访问次数和时间,通过计算实现,当然我们也可以自己定义频率控制方法。基本原理如下:

启用频率,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次

rest_framework.throttling <span style="color: #0000ff;">class<span style="color: #000000;"> VisitThrottle(SimpleRateThrottle):
<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;"&gt;def</span><span style="color: #000000;"&gt; get_cache_key(self,request,view):
    </span><span style="color: #0000ff;"&gt;return</span> self.get_ident(request)</pre>

2.settings.py中配置全局频率控制

REST_FRAMEWORK = :[], :,

urls.py

django.conf.urls django.contrib app01 urlpatterns =<span style="color: #000000;"> [

url(r</span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;^api/v1/auth</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;,views.AuthView.as_view()),url(r</span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;^api/v1/order</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;,views.OrderView.as_view()),]</span></pre>

models.py

django.db <span style="color: #0000ff;">class<span style="color: #000000;"> UserInfo(models.Model):
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)

订单视图

utils.permissions = [Authentication,] permission_classes = [MyPremission,] get(self,*args,** ret = {:1000,:,: JsonResponse(ret,safe=True)

使用postman验证如下图,可以看到频率限制已经起作用了。

四、频率控制源码剖析在前面几篇文章中已经分析了DRF的认证、权限源码,频率控制也一样也从APIView的dispatch方法说起,参考注解:

dispatch()

dispatch(self,**== request = self.initialize_request(request,**== self.default_response_headers
    <span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt;:
        self.initial(request,</span>*args,**<span style="color: #000000;"&gt;kwargs)

        </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Get the appropriate handler method</span>
        <span style="color: #0000ff;"&gt;if</span> request.method.lower() <span style="color: #0000ff;"&gt;in</span><span style="color: #000000;"&gt; self.http_method_names:
            handler </span>=<span style="color: #000000;"&gt; getattr(self,request.method.lower(),self.http_method_not_allowed)
        </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt;:
            handler </span>=<span style="color: #000000;"&gt; self.http_method_not_allowed

        response </span>= handler(request,**<span style="color: #000000;"&gt;kwargs)

    </span><span style="color: #0000ff;"&gt;except</span><span style="color: #000000;"&gt; Exception as exc:
        response </span>=<span style="color: #000000;"&gt; self.handle_exception(exc)

    self.response </span>= self.finalize_response(request,response,**<span style="color: #000000;"&gt;kwargs)
    </span><span style="color: #0000ff;"&gt;return</span> self.response</pre>

2.执行inital方法,initial方法中执行check_throttles则开始频率控制

initial(self,**= self.get_format_suffix(** </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Perform content negotiation and store the accepted info on the request</span> neg =<span style="color: #000000;"&gt; self.perform_content_negotiation(request) request.accepted_renderer,request.accepted_media_type </span>=<span style="color: #000000;"&gt; neg </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Determine the API version,if versioning is in use.</span> version,scheme = self.determine_version(request,**<span style="color: #000000;"&gt;kwargs) request.version,request.versioning_scheme </span>=<span style="color: #000000;"&gt; version,scheme </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Ensure that the incoming request is permitted</span> <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt;2.实现认证</span>

<span style="color: #000000;"> self.perform_authentication(request)
<span style="color: #008000;">#<span style="color: #008000;">3.权限判断
<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方法返回值判断频率是否通过

throttle self.get_throttles(): throttle.allow_request(request,self): self.throttled(request,throttle.wait())

4.get_throttles方法,采用列表生成式生成频率控制对象,与认证、权限一直

[throttle() throttle self.throttle_classes]

5.self.throttle_classes属性获取

</span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; The following policies may be set at either globally,or per-view.</span> renderer_classes =<span style="color: #000000;"&gt; api_settings.DEFAULT_RENDERER_CLASSES parser_classes </span>=<span style="color: #000000;"&gt; api_settings.DEFAULT_PARSER_CLASSES authentication_classes </span>=<span style="color: #000000;"&gt; 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;"&gt;"""</span><span style="color: #000000;"&gt; cache </span>=<span style="color: #000000;"&gt; default_cache <span style="color: #ff9900;"&gt;# 存放请求时间,类似与示例中的大字典,这里使用的是django的缓存</span> timer </span>=<span style="color: #000000;"&gt; time.time cache_format </span>= <span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;throttle_%(scope)s_%(ident)s</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt; scope </span>=<span style="color: #000000;"&gt; None THROTTLE_RATES </span>=<span style="color: #000000;"&gt; api_settings.DEFAULT_THROTTLE_RATES </span><span style="color: #0000ff;"&gt;def</span> <span style="color: #800080;"&gt;__init__</span><span style="color: #000000;"&gt;(self): </span><span style="color: #0000ff;"&gt;if</span> <span style="color: #0000ff;"&gt;not</span> getattr(self,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;rate</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;,None): self.rate </span>=<span style="color: #000000;"&gt; self.get_rate() self.num_requests,self.duration </span>=<span style="color: #000000;"&gt; self.parse_rate(self.rate) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_cache_key(self,view):<span style="color: #ff9900;"&gt; # 获取请求的key标识,必须要有否则会报错,这里可以重写,使用用户的用户名、或其他作为key,在示例中使用的get_ident方法用户获取用户IP作为key </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; 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;"&gt;"""</span> <span style="color: #0000ff;"&gt;raise</span> NotImplementedError(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;.get_cache_key() must be overridden</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_rate(self): <span style="color: #ff9900;"&gt;# 获取配置文件的配置速率 </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Determine the string representation of the allowed request rate. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span> <span style="color: #0000ff;"&gt;not</span> getattr(self,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;scope</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;,None): <span style="color: #ff9900;"&gt;# 通过获取共有属性scope来获取配置的速率</span> msg </span>= (<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;You must set either `.scope` or `.rate` for '%s' throttle</span><span style="color: #800000;"&gt;"</span> %<span style="color: #000000;"&gt; self.</span><span style="color: #800080;"&gt;__class__</span>.<span style="color: #800080;"&gt;__name__</span><span style="color: #000000;"&gt;) </span><span style="color: #0000ff;"&gt;raise</span><span style="color: #000000;"&gt; ImproperlyConfigured(msg) </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt;: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.THROTTLE_RATES[self.scope] </span><span style="color: #0000ff;"&gt;except</span><span style="color: #000000;"&gt; KeyError: msg </span>= <span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;No default throttle rate set for '%s' scope</span><span style="color: #800000;"&gt;"</span> %<span style="color: #000000;"&gt; self.scope </span><span style="color: #0000ff;"&gt;raise</span><span style="color: #000000;"&gt; ImproperlyConfigured(msg) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; parse_rate(self,rate): <span style="color: #ff9900;"&gt;# 格式化速率 </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Given the request rate string,return a two tuple of: <allowed number of requests>,<period of time in seconds> </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span> rate <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; (None,None) num,period </span>= rate.split(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;/</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;) <span style="color: #ff9900;"&gt;# 分离字符串</span> num_requests </span>=<span style="color: #000000;"&gt; int(num) duration </span>= {<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;s</span><span style="color: #800000;"&gt;'</span>: 1,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;m</span><span style="color: #800000;"&gt;'</span>: 60,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;h</span><span style="color: #800000;"&gt;'</span>: 3600,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;d</span><span style="color: #800000;"&gt;'</span>: 86400<span style="color: #000000;"&gt;}[period[0]] <span style="color: #ff9900;"&gt;# 转换时间为数字,示例配置的5/m,m转为60秒 </span></span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; (num_requests,duration) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; allow_request(self,view): <span style="color: #ff9900;"&gt; # 判断请求的速率是否通过 </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; 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;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span> self.rate <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; True self.key </span>=<span style="color: #000000;"&gt; self.get_cache_key(request,view) </span><span style="color: #0000ff;"&gt;if</span> self.key <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; True self.history </span>=<span style="color: #000000;"&gt; self.cache.get(self.key,[]) self.now </span>=<span style="color: #000000;"&gt; self.timer() </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Drop any requests from the history which have now passed the</span> <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; throttle duration</span> <span style="color: #0000ff;"&gt;while</span> self.history <span style="color: #0000ff;"&gt;and</span> self.history[-1] <= self.now -<span style="color: #000000;"&gt; self.duration: <span style="color: #ff9900;"&gt;# 频率判断实现原理,已经举例进行了说明</span> self.history.pop() </span><span style="color: #0000ff;"&gt;if</span> len(self.history) >=<span style="color: #000000;"&gt; self.num_requests: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.throttle_failure() </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.throttle_success() </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; throttle_success(self): <span style="color: #ff9900;"&gt;# 频率通过返回true </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Inserts the current request's timestamp along with the key into the cache. </span><span style="color: #800000;"&gt;"""</span><span style="color: #000000;"&gt; self.history.insert(0,self.now) self.cache.set(self.key,self.history,self.duration) </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; True </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; throttle_failure(self): <span style="color: #ff9900;"&gt;# 不通过返回false </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Called when a request to the API has failed due to throttling. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; False </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; wait(self): <span style="color: #ff9900;"&gt; # 返回等待时间 </span></span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Returns the recommended next request time in seconds. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; self.history: remaining_duration </span>= self.duration - (self.now - self.history[-1<span style="color: #000000;"&gt;]) </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt;: remaining_duration </span>=<span style="color: #000000;"&gt; self.duration available_requests </span>= self.num_requests - len(self.history) + 1 <span style="color: #0000ff;"&gt;if</span> available_requests <=<span style="color: #000000;"&gt; 0: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; None </span><span style="color: #0000ff;"&gt;return</span> remaining_duration / float(available_requests)</pre>

get_ident方法源码,该方法用于获取请求的IP:

0. If not use all of HTTP_X_FORWARDED_FOR if it is available,if not use REMOTE_ADDR. = request.META.get(= request.META.get( num_proxies = </span><span style="color: #0000ff;"&gt;if</span> num_proxies <span style="color: #0000ff;"&gt;is</span> <span style="color: #0000ff;"&gt;not</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;if</span> num_proxies == 0 <span style="color: #0000ff;"&gt;or</span> xff <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; remote_addr addrs </span>= xff.split(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;,</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;) client_addr </span>= addrs[-<span style="color: #000000;"&gt;min(num_proxies,len(addrs))] </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; client_addr.strip() </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #800000;"&gt;''</span>.join(xff.split()) <span style="color: #0000ff;"&gt;if</span> xff <span style="color: #0000ff;"&gt;else</span> remote_addr</pre>
五、内置频率控制类DRF内置了多种频率控制类提供我们使用,其核心原理都是通过判断request_allow方法返回值来判断频率是否通过,通过wait方法返回等待时间。

1.BaseThrottle:最基本的频率控制需要重写allow_request方法和wait方法

<span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; allow_request(self,view):
    </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt;
    Return `True` if the request should be allowed,`False` otherwise.
    </span><span style="color: #800000;"&gt;"""</span>
    <span style="color: #0000ff;"&gt;raise</span> NotImplementedError(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;.allow_request() must be overridden</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;)

</span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_ident(self,if not use REMOTE_ADDR.
    </span><span style="color: #800000;"&gt;"""</span><span style="color: #000000;"&gt;
    xff </span>= request.META.get(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;HTTP_X_FORWARDED_FOR</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;)
    remote_addr </span>= request.META.get(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;REMOTE_ADDR</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;)
    num_proxies </span>=<span style="color: #000000;"&gt; api_settings.NUM_PROXIES

    </span><span style="color: #0000ff;"&gt;if</span> num_proxies <span style="color: #0000ff;"&gt;is</span> <span style="color: #0000ff;"&gt;not</span><span style="color: #000000;"&gt; None:
        </span><span style="color: #0000ff;"&gt;if</span> num_proxies == 0 <span style="color: #0000ff;"&gt;or</span> xff <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None:
            </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; remote_addr
        addrs </span>= xff.split(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;,len(addrs))]
        </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; client_addr.strip()

    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #800000;"&gt;''</span>.join(xff.split()) <span style="color: #0000ff;"&gt;if</span> xff <span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt; remote_addr

</span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; wait(self):
    </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt;
    Optionally,return a recommended number of seconds to wait before
    the next request.
    </span><span style="color: #800000;"&gt;"""</span>
    <span style="color: #0000ff;"&gt;return</span> None</pre>

2.SimpleRateThrottle:示例中已经使用,并对源码和原理进行了分析。

Previous request information used for throttling is stored in the cache. </span><span style="color: #800000;"&gt;"""</span><span style="color: #000000;"&gt; cache </span>=<span style="color: #000000;"&gt; default_cache timer </span>=<span style="color: #000000;"&gt; time.time cache_format </span>= <span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;throttle_%(scope)s_%(ident)s</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt; scope </span>=<span style="color: #000000;"&gt; None THROTTLE_RATES </span>=<span style="color: #000000;"&gt; api_settings.DEFAULT_THROTTLE_RATES </span><span style="color: #0000ff;"&gt;def</span> <span style="color: #800080;"&gt;__init__</span><span style="color: #000000;"&gt;(self): </span><span style="color: #0000ff;"&gt;if</span> <span style="color: #0000ff;"&gt;not</span> getattr(self,view): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; 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;"&gt;"""</span> <span style="color: #0000ff;"&gt;raise</span> NotImplementedError(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;.get_cache_key() must be overridden</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_rate(self): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Determine the string representation of the allowed request rate. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span> <span style="color: #0000ff;"&gt;not</span> getattr(self,None): msg </span>= (<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;You must set either `.scope` or `.rate` for '%s' throttle</span><span style="color: #800000;"&gt;"</span> %<span style="color: #000000;"&gt; self.</span><span style="color: #800080;"&gt;__class__</span>.<span style="color: #800080;"&gt;__name__</span><span style="color: #000000;"&gt;) </span><span style="color: #0000ff;"&gt;raise</span><span style="color: #000000;"&gt; ImproperlyConfigured(msg) </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt;: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.THROTTLE_RATES[self.scope] </span><span style="color: #0000ff;"&gt;except</span><span style="color: #000000;"&gt; KeyError: msg </span>= <span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;No default throttle rate set for '%s' scope</span><span style="color: #800000;"&gt;"</span> %<span style="color: #000000;"&gt; self.scope </span><span style="color: #0000ff;"&gt;raise</span><span style="color: #000000;"&gt; ImproperlyConfigured(msg) </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; parse_rate(self,rate): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Given the request rate string,period </span>= rate.split(<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;/</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;) num_requests </span>=<span style="color: #000000;"&gt; int(num) duration </span>= {<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;s</span><span style="color: #800000;"&gt;'</span>: 1,<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;d</span><span style="color: #800000;"&gt;'</span>: 86400<span style="color: #000000;"&gt;}[period[0]] </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; (num_requests,view): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; 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;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span> self.rate <span style="color: #0000ff;"&gt;is</span><span style="color: #000000;"&gt; None: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; True self.key </span>=<span style="color: #000000;"&gt; self.get_cache_key(request,[]) self.now </span>=<span style="color: #000000;"&gt; self.timer() </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Drop any requests from the history which have now passed the</span> <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; throttle duration</span> <span style="color: #0000ff;"&gt;while</span> self.history <span style="color: #0000ff;"&gt;and</span> self.history[-1] <= self.now -<span style="color: #000000;"&gt; self.duration: self.history.pop() </span><span style="color: #0000ff;"&gt;if</span> len(self.history) >=<span style="color: #000000;"&gt; self.num_requests: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.throttle_failure() </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.throttle_success() </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; throttle_success(self): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Inserts the current request's timestamp along with the key into the cache. </span><span style="color: #800000;"&gt;"""</span><span style="color: #000000;"&gt; self.history.insert(0,self.duration) </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; True </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; throttle_failure(self): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Called when a request to the API has failed due to throttling. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; False </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; wait(self): </span><span style="color: #800000;"&gt;"""</span><span style="color: #800000;"&gt; Returns the recommended next request time in seconds. </span><span style="color: #800000;"&gt;"""</span> <span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; self.history: remaining_duration </span>= self.duration - (self.now - self.history[-1<span style="color: #000000;"&gt;]) </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt;: remaining_duration </span>=<span style="color: #000000;"&gt; self.duration available_requests </span>= self.num_requests - len(self.history) + 1 <span style="color: #0000ff;"&gt;if</span> available_requests <=<span style="color: #000000;"&gt; 0: </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; None </span><span style="color: #0000ff;"&gt;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;"&gt;"""</span><span style="color: #000000;"&gt; scope </span>= <span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;anon</span><span style="color: #800000;"&gt;'</span> <span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_cache_key(self,view): </span><span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; request.user.is_authenticated: </span><span style="color: #0000ff;"&gt;return</span> None <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; Only throttle unauthenticated requests.</span> <span style="color: #0000ff;"&gt;return</span> self.cache_format %<span style="color: #000000;"&gt; { </span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;scope</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;: self.scope,</span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;ident</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;: 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;"&gt;"""</span><span style="color: #000000;"&gt; scope </span>= <span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;user</span><span style="color: #800000;"&gt;'</span> <span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; get_cache_key(self,view): </span><span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; request.user.is_authenticated: ident </span>=<span style="color: #000000;"&gt; request.user.pk </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt;: ident </span>=<span style="color: #000000;"&gt; self.get_ident(request) </span><span style="color: #0000ff;"&gt;return</span> self.cache_format %<span style="color: #000000;"&gt; { </span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;scope</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;: self.scope,</span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;ident</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;: ident }</span></pre>
六、自定义频率控制自定义频率控制无非实现request_allow方法和wait方法,你可以根据实际需求来定制你的频率控制,下面是示例:

rest_framework.throttling REQUEST_RECORD = {} <span style="color: #008000;">#<span style="color: #008000;"> 访问记录,可使用nosql数据库

<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;"&gt;def</span> <span style="color: #800080;"&gt;__init__</span><span style="color: #000000;"&gt;(self):
    self.history </span>=<span style="color: #000000;"&gt; None

</span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; allow_request(self,view):
    </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 获取用户ip (get_ident)</span>
    remote_addr =<span style="color: #000000;"&gt; self.get_ident(request)
    ctime </span>=<span style="color: #000000;"&gt; time.time()

    </span><span style="color: #0000ff;"&gt;if</span> remote_addr <span style="color: #0000ff;"&gt;not</span> <span style="color: #0000ff;"&gt;in</span><span style="color: #000000;"&gt; REQUEST_RECORD:
        REQUEST_RECORD[remote_addr] </span>= [ctime,]  <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 保持请求的时间,形式{ip:[时间,]}</span>
        <span style="color: #0000ff;"&gt;return</span> True  <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; True表示可以访问</span>
    <span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 获取当前ip的历史访问记录</span>
    history =<span style="color: #000000;"&gt; REQUEST_RECORD.get(remote_addr)

    self.history </span>=<span style="color: #000000;"&gt; history


    </span><span style="color: #0000ff;"&gt;while</span> history <span style="color: #0000ff;"&gt;and</span> history[-1] < ctime - 60<span style="color: #000000;"&gt;:
        </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 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;"&gt;def</span><span style="color: #000000;"&gt; wait(self):
    </span><span style="color: #800000;"&gt;'''</span><span style="color: #800000;"&gt;返回等待时间</span><span style="color: #800000;"&gt;'''</span><span style="color: #000000;"&gt;
    ctime </span>=<span style="color: #000000;"&gt; time.time()
    </span><span style="color: #0000ff;"&gt;return</span> 60 - (ctime - self.history[-1])</pre>
七、总结1.使用方法:

  • 继承BaseThrottle类
  • 重写request_allow方法和wait方法,request_allow方法返回true代表通过,否则拒绝,wait返回等待的时间

2.配置

= :[], <span style="color: #008000;">#<span style="color: #008000;">#单一视图使用
throttle_classes =<span style="color: #000000;"> [VisitThrottle,]

<span style="color: #008000;">#<span style="color: #008000;">#优先级
单一视图>全局

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读