
Django Rest Framework源码剖析(三)-----频率控制
一、简介承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡、提高服务器配置、通过代理限制访问频率等,但是django rest framework自身就提供了访问频率的控制,可以从代码本身做控制。二、频率控制内部原理概述django rest framework 中频率控制基本原理基于访问次数和时间,通过计算实现,当然我们也可以自己定义频率控制方法。基本原理如下:启用频率,DRF内部会有一个字典记录来访者的IP,以及访问时间最近几(通过配置)次的访问时间,这样确保每次列表中最后一个元素都是该用户请求的最早时间,形式如下:{IP1:[第三次请求时间,第二次请求时间,第一次请求时间,],IP2:[第二次请求时间,第一次请求时间,],.....}举例说明,比如我现在配置了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次#!/usr/bin/env python3#_*_ coding:utf-8 _*_#Author:wdfrom rest_framework.throttling import SimpleRateThrottleclass VisitThrottle(SimpleRateThrottle):"""5秒内最多访问三次"""scope = "WD" #settings配置文件中的key,用于获取配置的频率def get_cache_key(self, request, view):return self.get_ident(request)2.settings.py中配置全局频率控制REST_FRAMEWORK = {#频率控制配置"DEFAULT_THROTTLE_CLASSES":['utils.throttle.VisitThrottle'], #全局配置,"DEFAULT_THROTTLE_RATES":{'WD':'5/m', #速率配置每分钟不能超过5次访问,WD是scope定义的值,}}urls.pyfrom django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^api/v1/auth', views.AuthView.as_view()),url(r'^api/v1/order', views.OrderView.as_view()),]models.pyfrom django.db import modelsclass UserInfo(models.Model):user_type_choice = ((1,"普通用户"),(2,"会员"),)user_type = models.IntegerField(choices=user_type_choice)username = models.CharField(max_length=32,unique=True)password = models.CharField(max_length=64)class UserToken(models.Model):user = models.OneToOneField(to=UserInfo)token = models.CharField(max_length=64)订单视图class OrderView(APIView):'''查看订单'''from utils.permissions import MyPremissionauthentication_classes = [Authentication,] #添加认证permission_classes = [MyPremission,] #添加权限控制def get(self,request,*args,**kwargs):#request.user#request.authret = {'code':1000,'msg':"你的订单已经完成",'data':"买了一个mac"}return JsonResponse(ret,safe=True)使用postman验证如下图,可以看到频率限制已经起作用了。四、频率控制源码剖析在前面几篇文章中已经分析了DRF的认证、权限源码,频率控制也一样也从APIView的dispatch方法说起,参考注解:dispatch()def dispatch(self, request, *args, **kwargs):"""`.dispatch()` is pretty much the same as Django's regular dispatch,but with extra hooks for startup, finalize, and exception handling."""self.args = argsself.kwargs = kwargs#对原始request进行加工,丰富了一些功能#Request(# request,# parsers=self.get_parsers(),# authenticators=self.get_authenticators(),# negotiator=self.get_content_negotiator(),# parser_context=parser_context# )#request(原始request,[BasicAuthentications对象,])#获取原生request,request._request#获取认证类的对象,request.authticators#1.封装requestrequest = self.initialize_request(request, *args, **kwargs)self.request = requestself.headers = self.default_response_headers # deprecate?try:self.initial(request, *args, **kwargs)# Get the appropriate handler methodif request.method.lower() in self.http_method_names:handler = getattr(self, request.method.lower(),self.http_method_not_allowed)else:handler = self.http_method_not_allowedresponse = handler(request, *args, **kwargs)except Exception as exc:response = self.handle_exception(exc)self.response = self.finalize_response(request, response, *args, **kwargs)return self.response2.执行inital方法,initial方法中执行check_throttles则开始频率控制def initial(self, request, *args, **kwargs):"""Runs anything that needs to occur prior to calling the method handler."""self.format_kwarg = self.get_format_suffix(**kwargs)# Perform content negotiation and store the accepted info on the requestneg = self.perform_content_negotiation(request)request.accepted_renderer, request.accepted_media_type = neg# Determine the API version, if versioning is in use.version, scheme = self.determine_version(request, *args, **kwargs)request.version, request.versioning_scheme = version, scheme# Ensure that the incoming request is permitted#2.实现认证self.perform_authentication(request)#3.权限判断self.check_permissions(request)#4.频率限制self.check_throttles(request)3.下面是check_throttles源码,与认证、权限一样采用列表对象方式,通过判断allow_request方法返回值判断频率是否通过def check_throttles(self, request):"""Check if request should be throttled.Raises an appropriate exception if the request is throttled."""for throttle in self.get_throttles(): #循环频率控制类结果if not throttle.allow_request(request, self): #判断其中的allow_requestf返回结果,true则频率通过,否则返回等待多少秒可以访问self.throttled(request, throttle.wait())4.get_throttles方法,采用列表生成式生成频率控制对象,与认证、权限一直def get_throttles(self):"""Instantiates and returns the list of throttles that this view uses."""return [throttle() for throttle in self.throttle_classes] #列表生成式生成控制频率对象列表5.self.throttle_classes属性获取class APIView(View):# The following policies may be set at either globally, or