DRF节流

代码使用

utils.throttle

VISIT_REMOTE = {}
from rest_framework.throttling import BaseThrottle


class VisttThrottle(BaseThrottle):
    '''用户节流'''
    def __init__(self):
        self.history = None
        
    def allow_request(self, request, view):
        remote_addr = request.Meta.get('REMOTE_ADDR')
        # remote_addr = request._request.META.get('REMOTE_ADDR')
        # 当封装的request遇到自己没有的属性时,会到下面的的_request里面找
        from time import time
        ctime = time()
        if remote_addr not in VISIT_REMOTE:
            VISIT_REMOTE[remote_addr] = [ctime, ]
            return True  # 可以访问
        history = VISIT_REMOTE.get(remote_addr)
        # 如果访问过此网站, 就得到它访问的时间列表
        self.history = history
        
        while history and history[-1] < ctime - 60:
            # 可以访问, 最后一个时间没用了,因为下一个访问的肯定更大,留一个小的值没意义
            history.pop()
        
        if len(history) <= 3:
            return True
        
        def wait(self):
            ret = 60 - time() + self.history[-1]
            return ret

局部使用

class OrderView(APIView):
    throttle_classes = [节流类] 

    def get(self,request):

        ret = {'code':10000,'msg':None}
        try:
            pass # 具体视图函数逻辑
        except Exception as e:
            pass  # 抛出异常
        return JsonResponse(ret)

全局使用

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES':['utils.throttling.Mythrottle'],
}

以上是最基本的自定义

  • rest_framework中内置了访问频率的类
    from rest_framework.throttling import SimpleRateThrottle
    除此之外,还有其他的内置类
class BaseThrottle:...

class SimpleRateThrottle(BaseThrottle):...

class AnonRateThrottle(SimpleRateThrottle):...

class UserRateThrottle(SimpleRateThrottle):...

class ScopedRateThrottle(SimpleRateThrottle):...

SimpleRateThrottle源码剖析

  • 看其源码,知其内容。
    就拿简单的SimpleRateThrottle

当我们使用SimpleRateThrottle时,我们需要设置THROTTLE_RATES的值。

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):
        # 如果类中没有设置rate属性,就执行方法get_rate
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()

        # 如果类中有rate属性的值 ,就调用rate的解析函数parse_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.
        """
        # 返回一个缓存的key, 并且要是唯一标识
        raise NotImplementedError('.get_cache_key() must be overridden')

    def get_rate(self):
        """
        Determine the string representation of the allowed request rate.
        """
        # 如果rate属性的值得不到,scope属性的值也得不到,就抛出异常
        if not getattr(self, 'scope', None):
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            '''
            'DEFAULT_THROTTLE_RATES':{
                'MyThrottle':'3/m'   都是为了得到3/m这个值   代表1min中内访问3次,m代表分钟。
            }
            '''
            # scope 就是DEFAULT_THROTTLE_RATES这个列表的 键  
            # rate  就是访问的频率,可以直接写,也可以用scope
            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:
        , 
        """
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        # 由此可见,rate的类型为    num/period   根据不同情况,重写这个方法,将
        # 下面的duration重写,就可以自定义频率了
        
        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`.
        """
        # 经过上面一轮操作后,如果rate还是None, 就代表没有访问频率的限制
        if self.rate is None:
            return True
        
        # 用户的访问记录在缓存中, 一般ip作为唯一标识,也就是作为key ,这里就是得到这个key
        self.key = self.get_cache_key(request, view)
        # 如果这个key不存在, 那么就代表这个还没访问过此网站, 所以可以继续访问 True
        if self.key is None:
            return True
        # 得到了key,就可以拿到这个ip访问此网站的  时间列表
        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)

内置类的使用

定义内置类,在类中为rate属性赋值不推荐,或者为scope属性赋值,配合setting使用

在自定义的类中: rate = '3/m' 就像权限中的可以定义的字段message一样

  • 比较简单的,但是不利于更改,程序大了要找到这个并不容易

scope = 'MyThrottle' # 配合全局Settings中使用,可随时更改,方便查找,名字自取

REST_FRAMEWORK = {
     'DEFAULT_THROTTLE_RATES':{
         'MyThrottle':'3/m'
          #  格式scope:rate      
                                # rate格式   次数 / 规定时间
     },
}
  • 这个配置是为类的编写而定义的。

以上二者选其一,上面的都自定义了限流类,重写rate或scope+setting,为rate赋值是将这个限流类规定为固定rate的限流类,而为scope赋值赋的值一般是限流类的名字,反正是起个名字,不在类中指定具体的rate,在使用时,按照上面代码格式赋值。

若要继承SimpleRateThrottle类,就必须重写get_cache_key()返回一个唯一标识。

class MyThrottle(SimpleRateThrottle):
    rate = '3/m'   # 比较简单的,但是不利于更改,程序大了要找到这个并不容易
    scope = 'MyThrottle' # 配合全局Settings中使用,可随时更改,方便查找

  # 以上二者选其一
  # 重写get_cache_key方法,设置一个唯一标志的key。
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')


定义限流类时,局部+全局

  1. rate = 3/m
  2. scope = 'Name',settings中,
    REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES':{
    'MyThrottle':'3/m'
    },
    }

当使用节流类的时候,还是哪两种方法,全局 + 局部
'DEFAULT_THROTTLE_CLASSES' : '节流类路径'throttle_classes = [节流类]

这些类不能放在views中,所以可以新建一个package,里面放throttle.py,permission.py,authentic.py文件。

问题:

初始化视图类的时候同样是来到了perform_authentication,check_permission,check_throttle,为什么认证时返回的是request.user

当用户登录网站后,用户的认证往往执行一次就够了,后续不会再次执行,所以在执行perform_authentication时不能直接执行认证类实例.认证方法。取而代之的是通过纽带request来实现,将request.user是一个方法,他hasattr找request中是否有_user信息,有说明认证过,直接返回request._user,没有说明未认证,转到一个函数去执行认证实例的认证方法。认证是分开的,所以在request中有authenticators,而没有throttles,permissions,在执行认证类实例的认证方法时for遍历的就是authenticators,而权限和节流中执行的是self.get_permissons(),slef.get_throttles()。

你可能感兴趣的:(DRF节流)