zoukankan      html  css  js  c++  java
  • restframework 频率类源码流程 及 自定义频率类

    一.首先请求来到之后都要走到APIView继承View 自己重写的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 = args
            self.kwargs = kwargs
            #第一步对request进行加工(添加数据)  请求模块
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                #第二步: 处理版权信息   认证    权限    请求用户进行访问频率的限制    三大认证模块
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                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
                #第三步:执行:get/post/put/delete函数   响应模块
                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

    二.进而走到dispatch方法中的第二步self.initial(request,*args.**kwargs),其中有对请求用户访问频率的限制

        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 request
            neg = 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
            #认证
            self.perform_authentication(request)
            #权限
            self.check_permissions(request)
            #请求用户访问频率限制
            self.check_throttles(request)

    三.我们点开请求用户访问频率限制的方法 self.check_throttles(request) 做具体分析

    # 频率组件核心源码分析
    def check_throttles(self, request):
        throttle_durations = []  #这个列表是用来存放下一次访问还需等待多长时间的
        # 1)遍历配置的频率认证类,初始化得到一个个频率认证类对象(会调用频率认证类的 __init__() 方法)
        # 2)频率认证类对象调用 allow_request 方法,判断是否限次(没有限次可访问,限次不可访问)
        # 3)频率认证类对象在限次后,调用 wait 方法,获取还需等待多长时间可以进行下一次访问
        # 注:频率认证类都是继承 SimpleRateThrottle 类
        for throttle in self.get_throttles():
            if not throttle.allow_request(request, self):
                # 只要频率限制了,allow_request 返回False了,才会调用wait
                throttle_durations.append(throttle.wait())
    
                if throttle_durations:
                    # Filter out `None` values which may happen in case of config / rate
                    # changes, see #1438
                    durations = [
                        duration for duration in throttle_durations
                        if duration is not None
                    ]
    
                    duration = max(durations, default=None)
                    self.throttled(request, duration)

    1.在此源码中 我们看到了其用for循环遍历self.get_throttles()  将取出来的throttle做操作   因此 我们要弄清 self.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]   #此处 将self.throttle_classes中的throttle遍历出来后 加括号实例化成对象存放在列表中 throttle()

    2.看到此 我们又遇见了一层 self.throttle_classes 

    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES  #说明它是在settings.py 中的 APISettings配置好的类

    3.而去settings.py中找发现其是一个空列表,不难想象,drf并没有将频率类添加进来   我们可以尝试找找 ,然而找到了throtling.py

     'DEFAULT_THROTTLE_CLASSES': [],

     4.这个文件里一共写了5个类,其中最底层的是BaseThrolle,其次是SimpleRateThrottle,然而其他三个都是继承的SimpleRatThrottle,因此我们

    大致可以确定这三个就是频率类。假设我们将他们放置到了刚才的setting.py下的 'DEFAULT_THROTTLE_CLASSES': [], 空列表中,我们顺其自然的遍历此列表

    并加括号实例化。而我们点看这三个看他们都做了哪些初始化时 并没有发现__intit__方法,因而可以断定此方法在父类中,这时我们可以点开父类

    SimpleRateThrottle进行分析:

    class SimpleRateThrottle(BaseThrottle):
        """
        A simple cache implementation, that only requires `.get_cache_key()`
        to be overridden.
    
        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.
        """
        cache = default_cache
        timer = time.time
        cache_format = 'throttle_%(scope)s_%(ident)s'
        scope = None
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    
        def __init__(self):
            if not getattr(self, 'rate', None):
                self.rate = self.get_rate()
            self.num_requests, self.duration = self.parse_rate(self.rate)
    
        def get_cache_key(self, request, view):
            """
            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.
            """
            raise NotImplementedError('.get_cache_key() must be overridden')
    
        def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                return self.THROTTLE_RATES[self.scope]
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
    
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            num, period = rate.split('/')
            num_requests = int(num)
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
            return (num_requests, duration)
    
        def allow_request(self, request, view):
            """
            Implement the check to see if the request should be throttled.
    
            On success calls `throttle_success`.
            On failure calls `throttle_failure`.
            """
            if self.rate is None:
                return True
    
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    
            self.history = self.cache.get(self.key, [])
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()
    
        def throttle_success(self):
            """
            Inserts the current request's timestamp along with the key
            into the cache.
            """
            self.history.insert(0, self.now)
            self.cache.set(self.key, self.history, self.duration)
            return True
    
        def throttle_failure(self):
            """
            Called when a request to the API has failed due to throttling.
            """
            return False
    
        def wait(self):
            """
            Returns the recommended next request time in seconds.
            """
            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)
    SimpleRateThrottle

    其中实例化的主要内容是

        cache = default_cache
        timer = time.time
        cache_format = 'throttle_%(scope)s_%(ident)s'
        scope = None
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    
        def __init__(self):
            if not getattr(self, 'rate', None):   #通过反射看自己类中是否有rate属性  没有就调用get_rate()方法 将返回值赋给rate
                self.rate = self.get_rate()
            self.num_requests, self.duration = self.parse_rate(self.rate)

    下面是get_rate(self)方法:

    def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                return self.THROTTLE_RATES[self.scope]    #需要从settings.py的THROTTLE_RATES字典中获取值  
    这里的scope是继承SimpleRateThrottle的类中的写好的属性 例如UserRateThrottle中 scope = 'user'

    except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg)
     # Throttling
        'DEFAULT_THROTTLE_RATES': {
            'user': None,
            'anon': None,
        },

    这里的value值都是空 是因为这让我们自定义写 访问频率的限制数据 例如 3/min

    紧接着通过parse_rate()方法 得到返回值 解压赋值给 num_requests(请求访问次数)   duration(请求频率限制的时间)

    到此每个频率类都实例化成功,并被循环遍历出来了。最后就是通过调用父类中的allow_request()方法 以及 wait()方法

    完成后面的频率限制操作

    总结:我们可以按照UserRateThrottle类中的get_cache_key方法来自定义频率限制类

    自定义频率限制类

    # 1) 自定义一个继承 SimpleRateThrottle 类 的频率类
    # 2) 设置一个 scope 类属性,属性值为任意见名知意的字符串
    # 3) 在settings配置文件中,配置drf的DEFAULT_THROTTLE_RATES,格式为 {scope字符串: '次数/时间'}
    # 4) 在自定义频率类中重写 get_cache_key 方法
        # 限制的对象返回 与限制信息有关的字符串
        # 不限制的对象返回 None (只能放回None,不能是False或是''等)

    短信接口 3/min 频率限制

    频率:api/throttles.py

    from rest_framework.throttling import SimpleRateThrottle
    
    class SMSRateThrottle(SimpleRateThrottle):
        scope = 'sms'
    
        # 只对提交手机号的get方法进行限制
        def get_cache_key(self, request, view):
            mobile = request.query_params.get('mobile')
            # 没有手机号,就不做频率限制
            if not mobile:
                return None
            # 返回可以根据手机号动态变化,且不易重复的字符串,作为操作缓存的key
            return 'throttle_%(scope)s_%(ident)s' % {'scope': self.scope, 'ident': mobile}

    配置:settings.py

    # drf配置
    REST_FRAMEWORK = {
        # 频率限制条件配置
        'DEFAULT_THROTTLE_RATES': {
            'sms': '1/min'
        },
    }

    视图:views.py

    from .throttles import SMSRateThrottle
    class TestSMSAPIView(APIView):
        # 局部配置频率认证
        throttle_classes = [SMSRateThrottle]
        def get(self, request, *args, **kwargs):
            return APIResponse(0, 'get 获取验证码 OK')
        def post(self, request, *args, **kwargs):
            return APIResponse(0, 'post 获取验证码  OK')

    路由:api/url.py

    url(r'^sms/$', views.TestSMSAPIView.as_view()),

    限制的接口

    #由于我们用的是request.query_params.get()方法获取的  
    #
    只会对 /api/sms/?mobile=具体手机号 接口才会有频率限制 # 1)对 /api/sms/ 或其他接口发送无限制 # 2)对数据包提交mobile的/api/sms/接口无限制 # 3)对不是mobile(如phone)字段提交的电话接口无限制
    万般皆下品,唯有读书高!
  • 相关阅读:
    桟错误分析方法
    gstreamer调试命令
    sqlite的事务和锁,很透彻的讲解 【转】
    严重: Exception starting filter struts2 java.lang.NullPointerException (转载)
    eclipse 快捷键
    POJ 1099 Square Ice
    HDU 1013 Digital Roots
    HDU 1087 Super Jumping! Jumping! Jumping!(动态规划)
    HDU 1159 Common Subsequence
    HDU 1069 Monkey and Banana(动态规划)
  • 原文地址:https://www.cnblogs.com/s686zhou/p/11730022.html
Copyright © 2011-2022 走看看