首页 > 其他分享 >【DRF-06】rest-framework之节流

【DRF-06】rest-framework之节流

时间:2024-05-27 22:45:32浏览次数:10  
标签:06 self request rest framework rate return throttle history

  • 1.自定义节流类,基于用户IP限制访问频率
    • 1.1:自定义节流类
import time
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
    '''
        #(1)取出访问者ip
        #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
        #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
        #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
     '''

    def __init__(self):
        self.history = None

    def allow_request(self,request,view):
        # 1. 获取用户IP
        remote_addr = self.get_ident(request)

        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True
        history = VISIT_RECORD.get(remote_addr)
        self.history = history

        while history and history[-1] < ctime - 60:
            history.pop()

        if len(history) < 3:
            history.insert(0,ctime)
            return True

        # return True    # 表示可以继续访问
        # return False # 表示访问频率太高,被限制

    def wait(self):
        # 还需要等多少秒才能访问
        ctime = time.time()
        return 60 - (ctime - self.history[-1])
  • 1.2:使用
class AuthView(APIView):
    """
    用于用户登录认证
    """
    authentication_classes = []
    permission_classes = []
    throttle_classes = [VisitThrottle,]
    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            print(user,pwd)
            print(request._request.POST)
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = "用户名或密码错误"
            # 为登录用户创建token
            token = md5(user)
            # 存在就更新,不存在就创建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '请求异常'

        return JsonResponse(ret)
  • 1.3:效果

  • 2.内置节流类

    • 2.1:自定义类继承:SimpleRateThrottle,实现:get_cache_key、scope = "keyvalue"(配置文件中的key)
    • 2.2:定义
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
    scope = "no_login"

    def get_cache_key(self, request, view):
        return self.get_ident(request)
  • 2.3:setting.py
REST_FRAMEWORK = {
    # 全局使用的认证类
    "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication','app01.utils.auth.Authtication', ],
    # "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication', ],
    # "UNAUTHENTICATED_USER":lambda :"匿名用户"
    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
    "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission',],
    "DEFAULT_THROTTLE_CLASSES":["app01.utils.throttle.VisitThrottle"],
    "DEFAULT_THROTTLE_RATES":{
        "NoLogin":'3/m',
    },
}
  • 3.节流源码流程
def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    throttle_durations = []
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            throttle_durations.append(throttle.wait())
  • 4.内置节流类源码
    • 4.1:源码节流类
    • 4.2:请求进来,先执行SimpleRateThrottle的__init__()方法
class SimpleRateThrottle(BaseThrottle):
    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)
  • 4.3:执行get_rate()赋值给rate
 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]     # 全局配置DEFAULT_THROTTLE_RATES
    except KeyError:
        msg = "No default throttle rate set for '%s' scope" % self.scope
        raise ImproperlyConfigured(msg)
  • 4.3:执行parse_rate方法,赋值给self.num_requests, self.duration,分别为次数,时间
 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)
  • 4.4:执行allow_request和await方法
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

    # 如果没有get_cache_key方法,直接返回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 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
  • 4.5:节流类,在setting中最好不要配置多个自定义类,例如:匿名用户控制3/m,登录用户10/m,只会走一个。

标签:06,self,request,rest,framework,rate,return,throttle,history
From: https://www.cnblogs.com/xwltest/p/18216735

相关文章

  • 代码随想录算法训练营第三十七天 | 860.柠檬水找零、406.根据身高重建队列、452.用最
    目录860.柠檬水找零思路代码 406.根据身高重建队列思路代码452.用最少数量的箭引爆气球思路代码860.柠檬水找零本题看上好像挺难,其实挺简单的,大家先尝试自己做一做。代码随想录思路    这题还有什么难不难的,这道题不是非常经典的入门题吗。......
  • 2000.1-2022.06.17中国经济政策不确定性指数日度数据
    2000.1-2022.06.17中国经济政策不确定性指数数据(日度)1、时间:2001.1.1-2022.06.172、指标:CNEPU(经济政策不确定性指数)3、来源:ChinaEconomicPolicyUncertaintyIndex4、用途:可用于量化我国经济政策的不确定性,预测宏观经济增长,分析政策波动对企业的影响5、指标解释:中国经济......
  • 基于.NET Framework 4.8.1的ASP.NET Web用Gitlab Runner调用MSBuild之后没有bin\rosl
    摘要基于.NETFramework4.8.1的传统ASP.NETWeb程序,使用GitlabRunner自动集成,在发布的网站目录下,没有bin\Roslyn文件夹。这里涉及到容易被忽视的Roslyn编译器的知识点。Roslyn是什么?在我们的ASP.NETWeb项目源代码中有什么体现?1、web.config下有配置节点一般在web.config末......
  • 接口报错.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework
    1、报文:.w.s.m.s.DefaultHandlerExceptionResolver:Resolved[org.springframework.http.converter.HttpMessageNotReadableException:JSONparseerror:Unexpectedcharacter('''(code39)):wasexpectingdouble-quotetostartfieldname;nestedex......
  • 06_整数拆分
    343.整数拆分给定一个正整数n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。返回你可以获得的最大乘积。示例1:输入:2输出:1解释:2=1+1,1×1=1。示例2:输入:10输出:36解释:10=3+3+4,3×3×4=36。说明:你可以假设n不小......
  • 梦断代码阅读笔记06
    梦断代码阅读笔记06阅读总结在阅读《梦断代码》这本书后,我深刻感受到编程不仅是一种技能,更是一种思维方式,它对日常生活中的问题解决和思考方式有着深远的影响。通过这本书,我学到了编程思维的重要性。书中强调了逻辑思维、创造性、自动化、数据分析和解决复杂问题的能力,这些都是......
  • hdu1069java
    给你n个方块,其中每个方块具有它的长宽高(方块可以任意旋转放置),方块数量不限。现在你要堆一个高塔,上面方块的长和宽必须严格小于下面方块的长和宽。问你能堆起来的最大高度。先将方块以长和宽按从小到大排序,然后从小到大以此为底,求出最大高度。dp[i]=max(dp[j])+i.height(j.x<i.x......
  • P3406 海底高铁(C++)
    海底高铁题目描述该铁路经过NNN个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。......
  • 代码随想录算法训练营第三天 |203、707、206
    链表基础理论:https://programmercarl.com/链表理论基础.html203题目链接:https://leetcode.cn/problems/remove-linked-list-elements/203代码随想录:https://programmercarl.com/0203.移除链表元素.html#算法公开课707题目链接:https://leetcode.cn/problems/design-linked-lis......
  • 代码随想录算法训练营第第18天 | 513.找树左下角的值 、112. 路径总和 、106.从中
    找树左下角的值本地递归偏难,反而迭代简单属于模板题,两种方法掌握一下题目链接/文章讲解/视频讲解:https://programmercarl.com/0513.找树左下角的值.html/***Definitionforabinarytreenode.*functionTreeNode(val,left,right){*this.val=(val===undef......