Loading

Django drf限流

Django drf限流

16.1 全局配置

配置文件settings.py

REST_FRAMEWORK = {

    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',  # 匿名用户,未登录的
        'rest_framework.throttling.UserRateThrottle'  # 经过登录之后的用户
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '2/minute',
        'user': '5/minute',
    }
}

视图

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response

class ExampleView2(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]

    def get(self, request):
        print(request.user.id)
        return Response({"message": "您是已登录用户,欢迎您使用高级功能"})

路由

from django.urls import path
from . import views

urlpatterns = [
    path('throt/', views.ExampleView2.as_view()),

]

访问效果

未登录用户第3次访问时:

{
    "detail": "请求超过了限速。 Expected available in 56 seconds."
}

已登录用户第6次访问的时候:

HTTP 429 Too Many Requests
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Retry-After: 55
Vary: Accept

{
    "detail": "请求超过了限速。 Expected available in 55 seconds."
}

16.2 局部配置

配置文件settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'anon': '2/minute', # 未登录用户每分钟访问2次
        'user': '5/minute', # 登录用户每分钟访问5次
    }
}

视图

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle

class ExampleView3(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]
	throttle_classes = [UserRateThrottle, AnonRateThrottle]
    def get(self, request):
        print(request.user.id)
        return Response({"message": "您是已登录用户,欢迎您使用高级功能"})

路由

from django.urls import path
from . import views

urlpatterns = [
    path('throt2/', views.ExampleView3.as_view()),

]

访问效果

未登录用户第3次访问ExampleView3时:

{
    "detail": "请求超过了限速。 Expected available in 58 seconds."
}

已登录用户第6次访问ExampleView3的时候:

HTTP 429 Too Many Requests
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Retry-After: 56
Vary: Accept

{
    "detail": "请求超过了限速。 Expected available in 56 seconds."
}

16.3 可选限流类

(1) AnonRateThrottle

限制所有匿名未认证用户,使用IP区分用户。

使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

(2)UserRateThrottle

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

使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

(3)ScopedRateThrottle (待定…)

限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。

ScopedRateThrottle的使用:

在settings.py配置文件中配置如下:

REST_FRAMEWORK = {
    
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    
    'DEFAULT_THROTTLE_RATES': {
        
        # 指定的视图类中设置:throttle_scope = 'contacts',即表示此接口每分钟最多访问10次
        'contacts': '10/m',
        
        # 指定的视图类中设置:throttle_scope = 'uploads',即表示此接口每分钟最多访问5次
        'uploads': '5/m'
    }
}

例如:

from rest_framework.views import APIView


class ContactListView(APIView):
    
    # 如此设置即表明此接口每分钟最多访问10次
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    # 如此设置即表明此接口每分钟最多访问10次
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    # 如此设置即表明此接口每分钟最多访问5次
    throttle_scope = 'uploads'
    ...

16.4 自定义限流组件

VISIT_RECORD = {} #定义全局变量,用于存放访问记录
class VisitThrottle(object):

    def __init__(self):     #用于await计算剩余访问时间
        self.history = None

    def allow_request(self,request,view):
        #获取用户ip作为唯一的标示
        remote_addr = request.META.get('REMOTE_ADDR')

        # 获取当前访问的时刻
        ctime = time.time()
        # 这是用户第一次访问,将其进行记录,并且返回True,允许继续访问
         if remote_addr not in VISIT_RECORD:
             VISIT_RECORD[remote_addr] = [ctime,]
             return True

        # 如果不是第一次访问,获取所有的记录
        history = VISIT_RECORD.get(remote_addr)

        self.history = history
        # 判断最开始的时刻与现在的时刻的差值是否在规定的时间范围内,比如在60s内,如果不在,
        # 可以去除最开始的时刻记录
        while history and history[-1] < ctime - 30:
            history.pop()
        # 此时列表中的时刻记录都是在规定的时间范围内,判断时刻的个数也就是访问的次数

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

    def wait(self):
        # 还需要等多少秒才能访问
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

在对应的视图中进行配置:

class BookView(ListAPIView):
    throttle_classes = [VisitThrottle,] #配置限流组件
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

限流的原理

在rest framework框架中,限流定义为类的列表,只需要全局配置或者局部配置即可。上述限流的原理就是以客户端的唯一标示作为键,以访问的时刻形成的列表作为值形成的字典,然后通过对字典进行操作:

{
    http://127.0.0.1:8020/ :[11:43:30,11:42:22,11:42:20,11:42:09]
}

  如上面的字典所示,后面的访问时间放插入到列表的最左侧,加入当前访问时间是11:43::30,那么与最开始访问时间11:42:09进行做差,然后与规定时间30s进行比较,如果不在30s内,那么就去除最左边的记录,同理使用while循环依次比较,最后在规定时间范围内的记录:

{
  http://127.0.0.1:8020/ :[11:43:30,]
}

再计算访问次数,也就是列表的个数,显然如果列表的个数小于3可以继续访问,否则不可以。

上面使用全局变量来进行记录,当然也是可以使用缓存来进行记录的存储,需要使用django的缓存API,from django.core.cache import cache,导入这个API后就可以使用set和get方法,设置和获取cache中存储的对象,只需要在操作全局变量除进行替换即可:

from django.core.cache import cache as default_cache
import time

class VisitThrottle(object):

    cache = default_cache
     
    def allow_request(self,request,view):
         ...
         ...    
        # 这是用户第一次访问,将其进行记录,并且返回True,允许继续访问

        if not self.cache.get(remote_addr):
            self.cache.set(remote_addr,[ctime,])
            return True
        # 如果不是第一次访问,获取所有的记录

        history = self.cache.get(remote_addr)
        self.history = history
        ...
        ...

rest framework的限流组件就是基于cache来完成的

上述的wait方法表示还需要多长时间可以进行访问这个API,对客户端的提示:

{
    "detail": "Request was throttled. Expected available in 56 seconds."
}
posted @ 2022-10-24 17:31  minqiliang  阅读(137)  评论(0编辑  收藏  举报
-->