ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

八、频率

2021-07-04 20:03:38  阅读:167  来源: 互联网

标签:throttle get self request rate 频率 scope


一、频率源码

    def check_throttles(self, request):
        
        # 用于存放不符合频率的情况
        throttle_durations = []
        
        # 调用get_throttles方法,获取频率组件列表
        for throttle in self.get_throttles():
            
            '''
            1. 频率对象必须存在allow_request方法
            2. allow_request传入两个参数,封装后的request, self代表当前视图类
            3. allow_request需要返回bool值,符合频率组件返回True,不符合返回False
            '''
            if not throttle.allow_request(request, self):
                
                # 频率对象需要存在wait()方法,以应对不符合频率限制的情况
                # wait方法需要返回一个时间结果,放入throttle_durations列表中
                # 抛出一个动态异常
                throttle_durations.append(throttle.wait())
 
        # 当存在不符合频率的情况时
        if throttle_durations:
            
            # 将throttle_durations列表中不为None的全部存入durations中
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]
			
            # 取出durations列表中最大值,如果没有最大值,返回None
            duration = max(durations, default=None)
            
            # 调用throttled方法,将封装request、duration传入
            self.throttled(request, duration)
            
    def get_throttles(self):
        return [throttle() for throttle in self.throttle_classes]
    
    
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    
    
    # 抛出异常,并动态显示时间
    def throttled(self, request, wait):
        raise exceptions.Throttled(wait)

二、drf频率类

from rest_framework.throttling import

1、BaseThrottle

作为基类,定义了基本的获取匿名用户IP的功能

class BaseThrottle:
	
    # 必须重写此方法,否则报错
    def allow_request(self, request, view):
        raise NotImplementedError('.allow_request() must be overridden')
	
    # 获取用户的唯一标识(IP)
    def get_ident(self, request):
       
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        
        # 获取访问用户的ip地址 
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()

        return ''.join(xff.split()) if xff else remote_addr

    # 必须重写此方法,此方法用于记录访问时间
    def wait(self):
        return None

2、SimpleRateThrottle

限制用户对于每个视图的访问频率,使用ip或user id

class SimpleRateThrottle(BaseThrottle):
    
    # django内置的缓存
    cache = default_cache	
    
    # 获取当前时间戳
    timer = time.time		
    
    # 
    cache_format = 'throttle_%(scope)s_%(ident)s'
    
    # 必须设置此值,作为获取DEFAULT_THROTTLE_RATES中频率设置的k,anon/user
    scope = None	
    
    # 获取配置的访问频率
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
	
第一步执行
    def __init__(self):
        # 判断是否存在rate属性/方法,此刻显然不存在
        if not getattr(self, 'rate', None):
            
            # Ⅰ、调用get_rate(),将其返回值赋予rate
            # get_rate()方法使用scope作为K,从api_settings.DEFAULT_THROTTLE_RATES中获取value
            self.rate = self.get_rate()
       	
        # Ⅱ、调用parse_rate(),返回一个二元组,并赋值给num_requests、duration
        self.num_requests, self.duration = self.parse_rate(self.rate)

    # Ⅰ、get_rate()的返回值赋予rate
    def get_rate(self):
        
        # 判断是否存在scope属性,并获取scope的值,如果scope为None,则抛出异常
        if not getattr(self, 'scope', None):
            
            # 异常中携带了当前类名名称
            msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                   self.__class__.__name__)
            raise ImproperlyConfigured(msg)

        try:
            # 从api_settings.DEFAULT_THROTTLE_RATES配置中,以scope作为k,获取value
            return self.THROTTLE_RATES[self.scope]
        
        # 如果api_settings.DEFAULT_THROTTLE_RATES中不存在scope的key,则抛出异常
        except KeyError:
            
            # 异常中携带了scope
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
	
    # Ⅱ、调用parse_rate(),返回一个二元组,并赋值给num_requests、duration
    def parse_rate(self, rate):
       
    	# 此刻rate为get_rate()方法的返回值
        # 如果rate为None,则抛出两个None
        if rate is None:
            return (None, None)
        
        # 如果rate不为None
        # 则将rate使用/分隔成一个列表,并将列表中的值赋予num, period
        num, period = rate.split('/')
        
        # 将num转换为int,赋予num_requests
        num_requests = int(num)
        
		# 只获取period中第一个字符,使用该字符作为K,获取其中对应的value
        # s 秒	m 分钟	h 小时	d 天
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        
        # 返回二元组,(次数,周期)
        return (num_requests, duration)
	
第二步执行
    def allow_request(self, request, view):
   		
        # 判断rate是否为None,如果为None,代表未设置频率周期及次数,则直接不限制频率,返回True
        if self.rate is None:
            return True
		
        # Ⅲ、调用get_cache_key方法,传入封装后request和view视图函数
        self.key = self.get_cache_key(request, view)
        
        # 如果get_cache_key方法没有返回值,则代表不限制频率,返回True
        if self.key is None:
            return True
		
        # cache = default_cache django内部的缓存
        # 获取self.key在django缓存中调用的时间列表
        self.history = self.cache.get(self.key, [])
        
        # timer = time.time		获取当前时间戳
        self.now = self.timer()

   		# 整个逻辑都是用于计算self.key的规定周期内的调用次数,是否超出频率设置
        '''
        history:获取当前IP/User在django缓存中调用的时间列表
        history[-1]:获取时间列表中最后一个时间戳
        now:当前时间戳
        duration:限定的访问频率之周期
        num_requests:限定的访问频率之次数
        '''
        while self.history and self.history[-1] <= self.now - self.duration:
            # 如果当前IP/User在django缓存中调用的时间列表存在值,且最后一个时间小于当前时间戳-限定的访问周期,即超出周期范围,则直接删除最后一个时间,属于无用时间
            self.history.pop()
         
        # 如果当前IP/User在django缓存中调用的时间列表中时间个数 >= 限定次数
        if len(self.history) >= self.num_requests:
            # Ⅳ、则调用throttle_failure方法,该方法抛出False,代表频率验证不通过
            return self.throttle_failure()
        
        # Ⅴ、如果未超出频率限定次数,则调用throttle_success方法
        return self.throttle_success()
    
	 # Ⅲ、get_cache_key方法必须重写,否则直接抛出异常
     def get_cache_key(self, request, view):  
        '''
        1. get_cache_key是用于内部返回出标识用户的唯一标识
        2. 对于匿名用户,可以直接调用父类BaseThrottle中的get_ident(self, request)方法,返回IP
        3. 对于登录用户,可以直接返回登录用户的用户名 request.user.username
        '''
        raise NotImplementedError('.get_cache_key() must be overridden')
	
    # Ⅴ、如果未超出频率限定次数,则调用throttle_success方法
    def throttle_success(self):
		
        # 将当前IP/User在django缓存中调用的时间列表首位增加当前时间,作为访问一次的记录
        self.history.insert(0, self.now)
        
        # cache = default_cache django内部的缓存
        # 在django内部缓存中,增加当前IP/User的新时间列表,新周期
        self.cache.set(self.key, self.history, self.duration)
        
        # 返回True,代表频率验证通过
        return True
	
    # Ⅳ、则调用throttle_failure方法,该方法抛出False,代表频率验证不通过
    def throttle_failure(self):  
        return False
	
第三步执行
    def wait(self):
        '''
        remaining_duration 可以看作剩余周期时间
        available_requests 可以看作剩余访问次数 + 1
        '''
  
  		# 如果当前IP/User在django缓存中调用的时间列表存在值
        if self.history:
            
            # remaining_duration = 周期 - (当前时间戳 - 时间列表最后一个时间戳)
            remaining_duration = self.duration - (self.now - self.history[-1])
        else:
            
            # remaining_duration = 周期
            remaining_duration = self.duration
		
        # available_requests = 次数 - 时间列表中时间个数 + 1
        available_requests = self.num_requests - len(self.history) + 1
        
        # 如果 available_requests <= 0 代表超出次数
        if available_requests <= 0:
            # 返回None
            return None
		
        # 返回 剩余周期时间 / 剩余访问次数 + 1 
        return remaining_duration / float(available_requests)

3、AnonRateThrottle

限制所有匿名用户,使用IP区分用户

'''
AnonRateThrottle继承自SimpleRateThrottle
在SimpleRateThrottle基础上,将get_cache_key()方法进行重写了
原SimpleRateThrottle中定义了get_cache_key()接口,此接口可以来确定限制匿名用户还是登录用户

AnonRateThrottle就是重写了get_cache_key(),只用于限制匿名用户
'''
class AnonRateThrottle(SimpleRateThrottle):

    scope = 'anon'

    def get_cache_key(self, request, view):
        # 判断当前用户是否登录(PS:匿名用户返回False,正常用户返回True)
        if request.user.is_authenticated:
            # 正常用户直接返回None
            return None  
		
        # cache_format = 'throttle_%(scope)s_%(ident)s'
        return self.cache_format % {
            'scope': self.scope,				# 传入scope
            'ident': self.get_ident(request)	# 传入匿名用户标识(IP)
        }

4、UserRateThrottle

限制认证用户,使用User id 来区分

'''
UserRateThrottle与AnonRateThrottle大差不差,都是继承自SimpleRateThrottle
并重写了get_cache_key方法,UserRateThrottle主要是限制认证用户的
'''
class UserRateThrottle(SimpleRateThrottle):

    scope = 'user'
	
    def get_cache_key(self, request, view):
        
        # 判断当前用户是否登录(PS:匿名用户返回False,正常用户返回True)
        if request.user.is_authenticated:
            
            # ident标识 = 当前用户的PK
            ident = request.user.pk
        else:
            
            # ident标识 = 匿名用户的IP
            ident = self.get_ident(request)

        return self.cache_format % {
            'scope': self.scope,	# 传入scope
            'ident': ident			# 传入匿名用户标识(IP)/当前用户的PK
        }

5、ScopedRateThrottle

'''
'''
class ScopedRateThrottle(SimpleRateThrottle):
 
    scope_attr = 'throttle_scope'

    def __init__(self):
        pass

    def allow_request(self, request, view):
		
        # 判断视图函数中是否存在throttle_scope方法/属性,显然不存在,所以值为None
        self.scope = getattr(view, self.scope_attr, None)
		
        # throttle_scope不存在,不会执行
        if not self.scope:
            return True
		
        # 调用父类get_rate()方法,获取api_settings.DEFAULT_THROTTLE_RATES配置中指定频率
        self.rate = self.get_rate()
        
        # 将频率分为 num_requests(次数)	duration(周期)
        self.num_requests, self.duration = self.parse_rate(self.rate)
		
        # 调用父类allow_request()方法,进行用户频率逻辑判断
        return super().allow_request(request, view)
	
    def get_cache_key(self, request, view):
		# 判断用户是否登录
        if request.user.is_authenticated:
            # 如果登录,则使用用户pk作为标识
            ident = request.user.pk
        else:
            # 如果未登录,则使用IP地址,作为标识
            ident = self.get_ident(request)
		
        return self.cache_format % {
            'scope': self.scope,	# 传入scope
            'ident': ident			# 传入匿名用户标识(IP)/当前用户的PK
        }

三、频率配置

from rest_framework import settings

# 全局配置-settings.py    
REMOVED_SETTINGS = [
    # 认证组件配置
    "DEFAULT_AUTHENTICATION_CLASSES":[
        "app01.认证类路径",
        ...
    ],
    
    # 权限组件配置
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.权限类路径',
        ...
    ],
    
    # 频率组件配置
    'DEFAULT_THROTTLE_CLASSES': [
        'app01.频率类路径',
        ...
    ],
    
    # 频率访问次数/用户设置
    'DEFAULT_THROTTLE_RATES': {
        'user': None,	# '10/m'	设置登录用户访问频率
        'anon': None,	# '3/m'		设置匿名用户访问频率
    },
]


# 局部配置,在视图类中写
throttle_classes=[
    '认证类',
    ...
]

# 局部禁用,在视图类中写
throttle_classes=[]

 

标签:throttle,get,self,request,rate,频率,scope
来源: https://www.cnblogs.com/zhangzhuowei/p/14969716.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有