流量控制的逻辑:
1、设置一个唯一标识,作为cache的key
2、请求访问时,构造唯一标识,从ceche中获取[时间1,时间2,时间3,…]
3、根据设置流量控制规则,如:5/m (一分钟最多访问5次),判断该唯一标识记录到cache中的访问时间的次数,是否满足请求的条件
4、不满足,就抛出异常,前端收到请求过快的响应。满足访问,就直接访问。
一、代码的准备
视图:
class TestAPIView(APIView):authentication_classes=[MyJWTAuthentication]permission_classes = [AdminPermission,]def get(self,request)return Respponse({'code':200,'msg':'测试通过'})
路由:
path('test/',views.TestAPIView.as_view())
请求方式:GET
流量控制类:
from rest_framework.throttling import SimpleRateThrottle#对请求源的IP进行限制
class IPThrottle(SimpleRateThrottle):# 在settings中配置频率时使用到的关键字,有scope来指定scope = 'IP'def get_cache_key(self, request, view):return f'{self.scope}-{self.get_ident(request)}-throttle'# 这里return什么,就以什么作为限制,这里限制ip,直接return ip就可以return request.META.get('REMOTE_ADDR')# 可以在这里设置访问频率# def get_rate(self):# return '3/m'
二、具体的流程
1、TestAPIView的as_view方法,当前实例对象没有,找父类的APIView的as_view
@classmethod
def as_view(cls, **initkwargs):#1、调用APIView父类的as_view方法view = super().as_view(**initkwargs)view.cls = clsview.initkwargs = initkwargs#2、去除掉csrf校验return csrf_exempt(view)
2、APIView的as_view是调用父类View的as_view方法
@classonlymethod
def as_view(cls, **initkwargs):def view(request, *args, **kwargs):self = cls(**initkwargs)self.setup(request, *args, **kwargs)if not hasattr(self, 'request'):raise AttributeError("%s instance has no 'request' attribute. Did you override ""setup() and forget to call super()?" % cls.__name__)return self.dispatch(request, *args, **kwargs)view.view_class = clsview.view_initkwargs = initkwargsreturn view#返回的是闭包view,
#view的功能就是self.dispatch()方法
3、as_view主要返回闭包,关于self.dispatch()方法
4、self是TestAPIView , 没有dispatch方法,调用父类APIView的dispatch方法
#简化后的源码
def dispatch(self, request, *args, **kwargs):#处理url中的参数self.args = argsself.kwargs = kwargs#构建drf的request对象request = self.initialize_request(request, *args, **kwargs)self.request = requestself.headers = self.default_response_headers # deprecate?try:#认证、权限、限流的执行self.initial(request, *args, **kwargs)#通过反射来执行TestAPIView中的get、post等方法if 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_allowed#视图函数执行结果response = handler(request, *args, **kwargs)except Exception as exc:response = self.handle_exception(exc)self.response = self.finalize_response(request, response, *args, **kwargs)return self.response
5、self.initial(request,),TestAPIView中没有,找APIView的initial方法,
def initial(self, request, *args, **kwargs):#认证、权限、限流self.perform_authentication(request)self.check_permissions(request)self.check_throttles(request)
6、TestAPIView没有check_throttles,调用APIView的check_throttles
def check_throttles(self, request):throttle_durations = []for throttle in self.get_throttles():if not throttle.allow_request(request, self):throttle_durations.append(throttle.wait())if throttle_durations:durations = [duration for duration in throttle_durationsif duration is not None]duration = max(durations, default=None)self.throttled(request, duration)
-
self.get_throttles(),TestAPIView没有,调用父类APIView的get_throttles()
-
def get_throttles(self):return [throttle() for throttle in self.throttle_classes]
-
-
具体流量控制了流程
- 获取到配置的[限流对象1,限流对象2,…]
- 执行限流对象.allow_request(request, self) 方法
7、SimpleRateThrottle的allow_request方法
class SimpleRateThrottle(BaseThrottle):def __init__(self):#获取限流的规则,如5/m , 没有就请求get_rate方法if not getattr(self, 'rate', None):self.rate = self.get_rate()#解析限制规则5/m, 允许访问的次数,允许的时间(秒)self.num_requests, self.duration = self.parse_rate(self.rate)def allow_request(self, request, view):if self.rate is None:return True#获取唯一标识,cache的keyself.key = self.get_cache_key(request, view)if self.key is None:return True#获取历史访问的列表[时间点1,时间点2]self.history = self.cache.get(self.key, [])self.now = self.timer()#把历史列表中,超过1分钟的时间点移除(5/m)while self.history and self.history[-1] <= self.now - self.duration:self.history.pop()#如果1分钟内访问的历史次数,大于等于规则5/m中的5,就是访问失败,抛出异常if len(self.history) >= self.num_requests:return self.throttle_failure()#否则访问超过return self.throttle_success()
- 1、实例化对象时,就解析限流规则,如5/m
- 2、执行allow_request方法时
- 判断符合要求的历史访问记录数是否大于等于5
- 符合:return True
- 不符合:return False
- 判断符合要求的历史访问记录数是否大于等于5
8、回到APIView.check_throttles()
def check_throttles(self, request):#记录要等待多少时间,才能请求throttle_durations = []for throttle in self.get_throttles():#如果限流类返回False,就把要等待时间放到throttle_durations列表中if not throttle.allow_request(request, self):throttle_durations.append(throttle.wait())if throttle_durations:durations = [duration for duration in throttle_durationsif duration is not None]#取要等待的最长时间duration = max(durations, default=None)self.throttled(request, duration)
9、限流类.wait() 方法
def wait(self):if self.history:remaining_duration = self.duration - (self.now - self.history[-1])else:remaining_duration = self.durationavailable_requests = self.num_requests - len(self.history) + 1if available_requests <= 0:return None#当前时间-最早一次符合的请求时间:还需要等待的时间return remaining_duration / float(available_requests)
10、回到APIView.dispatch方法
def dispatch(self, request, *args, **kwargs):self.args = argsself.kwargs = kwargsrequest = self.initialize_request(request, *args, **kwargs)self.request = requestself.headers = self.default_response_headers # deprecate?try:#认证、权限、限流self.initial(request, *args, **kwargs)#反射TestAPIView,获取get方法,执行get方法,if 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.response
- 通过反射,拿到TestAPIView中对应的视图函数,执行完成,拿到函数执行结果。