DRF-源码解析-4.2-限流的流程:drf的限流源码,drf流量控制流程,drf如何流量控制

DRF-源码解析-4.2-限流的流程:drf的限流源码,drf流量控制流程,drf如何流量控制_第1张图片

流量控制的逻辑:

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 = cls
    view.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 = cls
        view.view_initkwargs = initkwargs
    return 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 = args
        self.kwargs = kwargs
        #构建drf的request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.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_durations
                if 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的key
        self.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

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_durations
                if 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.duration

        available_requests = self.num_requests - len(self.history) + 1
        if available_requests <= 0:
            return None
		#当前时间-最早一次符合的请求时间:还需要等待的时间
        return remaining_duration / float(available_requests)

10、回到APIView.dispatch方法

    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.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_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
  • 通过反射,拿到TestAPIView中对应的视图函数,执行完成,拿到函数执行结果。

你可能感兴趣的:(django,python)