首页 > 编程语言 >drf源码剖析----限流

drf源码剖析----限流

时间:2024-03-09 23:55:46浏览次数:27  
标签:throttle self request ---- 限流 return 源码 def wait

点击查看代码
urlpatterns = [
# 1. 访问视图函数中的LoginView()类中的as_view()方法
    path('login/', views.LoginView.as_view()),  
]
点击查看代码
# 可自定义detail code
class Throttled(APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_detail = _('Request was throttled.')
    extra_detail_singular = _('Expected available in {wait} second.')
    extra_detail_plural = _('Expected available in {wait} seconds.')
    default_code = 'throttled'

    def __init__(self, wait=None, detail=None, code=None):
        if detail is None:
            detail = force_str(self.default_detail)
        if wait is not None:
            wait = math.ceil(wait)
            detail = ' '.join((
                detail,
                force_str(ngettext(self.extra_detail_singular.format(wait=wait),
                                   self.extra_detail_plural.format(wait=wait),
                                   wait))))
        self.wait = wait
        super().__init__(detail, code)
点击查看代码
class APIView(View):
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES

    def get_throttles(self):
       # 7. 优先获取类LoginView()的throttle_classe,没有再找全局配置
        return [throttle() for throttle in self.throttle_classes]

    def throttled(self, request, wait):
        raise exceptions.Throttled(wait)

    def check_throttles(self, request):  
        throttle_durations = []
        # 6. 返回限流类的实例化对象
        for throttle in self.get_throttles():
# 8. 类MyThrottling()里没有allow_request()方法,到父类SimpleRateThrottle()里找
            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) # 抛出异常

    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)  # 5. 限流组件

    @classmethod
    def as_view(cls, **initkwargs):
# 2. 访问父类View()类中的as_view()方法
        view = super().as_view(**initkwargs)  
        view.cls = cls
        view.initkwargs = initkwargs

        return csrf_exempt(view)

    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  

        try:
            # 4. 入口
            self.initial(request, *args, **kwargs)

            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
点击查看代码
class 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)
# 3. 返回闭包函数,类APIView()中的dispatch()方法
        return view 
点击查看代码
class LoginView(APIView):
    authentication_classes = []
    throttle_classes = [MyThrottling, ]

    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
        if not user_obj:
            return Response({'status': False, 'error': '用户不存在'})

        token = str(uuid.uuid4())
        user_obj.token = token
        user_obj.save()
        return Response({'status': True, 'data': token})
点击查看代码
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)

    def get_cache_key(self, request, view):
        raise NotImplementedError('.get_cache_key() must be overridden')

    def get_rate(self):
        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):
        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):
        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()
        # 剔除时间限制范围之外的访问记录
        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):
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True

    def throttle_failure(self):
        return False

    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)
点击查看代码
class MyThrottling(SimpleRateThrottle):
    scope = 'xxx'
    THROTTLE_RATES = {'xxx': '5/m'}
    cache = default_cache

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk
        else:
            ident = self.get_ident(request)
        return self.cache_format % {'scope': self.scope, 'ident': ident}

标签:throttle,self,request,----,限流,return,源码,def,wait
From: https://www.cnblogs.com/only-you-zta/p/18063458

相关文章

  • 计算机操作系统(第四版)- 汤小丹 - 课后习题答案
    计算机操作系统(第四版)汤小丹课后习题答案第一章1.设计现代OS的主要目标是什么?答: (1)有效性  (2)方便性  (3)可扩充性  (4)开放性2.OS的作用可表现在哪几个方面?答:(1)OS作为用户与计算机硬件系统之间的接口(2)OS作为计算机系统资源的管理者(3)OS......
  • AtCoder Beginner Contest 344
    AtCoderBeginnerContest344ABCD略EInsertorErase手写链表调了这么久。。链表模板。FEarntoAdvance考虑DP,但是我们发现不是很好转移,然后我们发现\(n\le80\),我们观察一下题目的性质。如果路径确定了,那么我们肯定会在最大值的地方使劲加到终点为止。那么我们考......
  • OpenWrt之Transmission报错
    OpenWrt之Transmission报错浏览器http://192.168.1.1:9091/transmission/输入后,报错为404,具体如下:Couldn'tfindTransmission'swebinterfacefiles!修复教程:进入路由器,编辑/etc/init.d/transmission这个文件在这个文件的快结尾处,注释掉这句procd_add_jailtransmissio......
  • 分化小数
     输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。a,b<=1000000,c<=100.输入包含多组数据,结束标记为a=b=c=0. #include<iostream>#include<iomanip>#include<sstream>intmain(){inta,b,c;std::stringresult;std::stringstreamss;......
  • rpmdb 常用命令初始化与重建rpm数据库
    在Linux系统中,rpmdb命令用于初始化和重建rpm数据库。这里有一些常用的rpmdb方法:初始化RPM数据库:rpmdb--initdb这个命令会创建一个新的RPM数据库,如果数据库已经存在,它不会做任何事情。重建RPM数据库:rpmdb--rebuilddb如果RPM数据库损坏或者需要更新,这个命令会从已安......
  • elasticsearch常用请求接口Rest API示例
    创建shopping索引PUT/shopping查看全部索引GET/_cat/indices查看指定索引GET/shopping删除指定索引DELETE/shopping索引的映射字段属性,是否关键字和加入索引PUT/shopping/_mapping{"properties":{"title":{"type":"text"},&qu......
  • 2024 年春节集训 _ 第一课 - 期望类型动态规划
    可能会用到的记号:\([P]=\begin{cases}1&(P成立)\\0&(P不成立)\end{cases}\)期望概率\(\texttt{dp}\)\(\texttt{dp}\)的变形当中最为简单易懂但是又思路又最为清奇。与之相关的难题数不胜数。考场上可以想出正解的都是超级神仙。粗浅的提一句,离散变量,也......
  • 2024 年春节集训 _ 第二课 - 数据结构优化动态规划
    【例题\(1\)】递增子序列\(\color{white}{link}\)考虑\(dp.\)\(dp[i][j]\)表示以元素\(i\)为结尾,长度为\(k\)的方案数。那么显而易见就有一个转移方程:\[dp[i][j]=\sum_{a[k]<a[i],\k<i}dp[k][j-1]\]先抛去第二维度的\(j\),这是可以做一个关于\(a[i]\)值的大......
  • abc344_D - String Bags 题解
    一个月没有碰oi,感觉水平已经退化到负的了。来复健一下。D-StringBagslink题意:给你\(n\)组字符串组,按\(1\)~\(n\)的顺序,对于每组字符串组,可从中至多选一个字符串。求能否用所选串按顺序拼接成指定串,以及选取字符串的最小个数。然后读完题发现是个\(01\)背包;对于第......
  • AT_abc344_e 题解
    本文同步发表于洛谷。赌狗天天输的一集。赛时各种【数据删除】原因导致没做出来。大意给你一个长度为\(N\)的序列\(A=(A_1,\ldots,A_N)\)。保证\(A\)中的元素是不同的。你要处理\(Q\)个操作。每个操作是以下两种类型之一:1xy:在\(A\)中元素\(x\)后面紧接着插入......