drf源码_限流

本文详细介绍了如何在DjangoRESTFramework中使用限流类,如SimpleRateThrottle和自定义类IpThrottle和UserThrottle,以及如何配置缓存和全局限速策略。通过示例展示了限流类的使用方法,包括基于IP和用户ID的访问频率控制。
摘要由CSDN通过智能技术生成

1、限流源码

from rest_framework.throttling import SimpleRateThrottle
# 使用缓存 存储用户姓名或者ip
from django.core.cache import cache as default_cache


# 通常我们使用限流类的时候,是继承 SimpleRateThrottle类,通常来说只需要 改写
# get_cache_key就 可以了


# 自定义限流类,分别根据Ip 和 用户名 限流

class IpThrottle(SimpleRateThrottle):
    # 匿名用户登录,根据ip记录
    scope = "ip"
    cache = default_cache

    def get_cache_key(self, request, view):
        # 获取访问者ip
        ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)
        return self.cache_format % {'scope': self.scope, 'ident': ident}


class UserThrottle(SimpleRateThrottle):
    scope = "user"
    cache = default_cache

    def get_cache_key(self, request, view):
        # 获取用户id
        ident = request.user.pk  # 用户ID
        return self.cache_format % {'scope': self.scope, 'ident': ident}



def check_throttles(self, request):
        throttle_durations = []
        # 循环获取每个限流类实例化对象
        for throttle in self.get_throttles():
            # 如果返回值为False时
            if not throttle.allow_request(request, self):
                # 建议下次访问网址的时间加入到throttle_durations 列表中
                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 get_throttles(self):
        return [throttle() for throttle in self.throttle_classes]


    def allow_request(self, request, view):
        
        # 初始化时已经给self.rate赋值了,为 5/m、10/m   表示 次数/单位
        if self.rate is None:
            return True
		# 返回throttle_%(scope)s_%(ident)
        # throttle_user_rengang
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True
		
        # 从缓存中获取throttle_user_rengang键的值,如果不存在就获得一个
        # 空列表
        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):
         # 如果还可以访问,那么就把现在的时间插入到history中
        self.history.insert(0, self.now)
         # 更新的redis中存的key
        self.cache.set(self.key, self.history, self.duration)
        return True

    def throttle_failure(self):

        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)

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 throttled(self, request, wait):
        """
        If request is throttled, determine what kind of exception to raise.
        """
        raise exceptions.Throttled(wait)  

2、限流使用

# 限流文件

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache


class IpThrottle(SimpleRateThrottle):
    scope = "ip"
    cache = default_cache

    def get_cache_key(self, request, view):
        ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)
        return self.cache_format % {'scope': self.scope, 'ident': ident}


class UserThrottle(SimpleRateThrottle):
    scope = "user"
    cache = default_cache

    def get_cache_key(self, request, view):
        ident = request.user.pk  # 用户ID
        return self.cache_format % {'scope': self.scope, 'ident': ident}


# 视图文件,限流的使用
import uuid
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from api import models
from ext import code
from ext.per import UserPermission, BossPermission, ManagerPermission
from ext.view import NbApiView
from ext.throttle import IpThrottle, UserThrottle


class LoginView(APIView):
    throttle_classes = [IpThrottle, ]
    authentication_classes = []

    def post(self, request):
        # 1.接收用户POST提交的用户名和密码
        # print(request.query_params)
        user = request.data.get("username")
        pwd = request.data.get("password")

        # 2.数据库校验
        user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
        if not user_object:
            return Response({"status": False, 'msg': "用户名或密码错误"})

        # 3.正确
        token = str(uuid.uuid4())
        user_object.token = token
        user_object.save()

        return Response({"status": True, 'data': token})


class UserView(NbApiView):
    # 经理、总监、用户
    permission_classes = [BossPermission, ManagerPermission, UserPermission]

    def get(self, request):
        print(request.user, request.auth)
        return Response("UserView")

    def post(self, request):
        print(request.user, request.auth)
        return Response("UserView")


class OrderView(NbApiView):
    # authentication_classes = [...]
    # 经理 或 总监
    permission_classes = [BossPermission, ManagerPermission]
    throttle_classes = [UserThrottle, IpThrottle]

    def get(self, request):
        print(request.user, request.auth)
        self.dispatch
        return Response({"status": True, "data": [11, 22, 33, 44]})


class AvatarView(NbApiView):
    # 总监 或 员工
    permission_classes = [BossPermission, UserPermission]
    throttle_classes = [UserThrottle, ]

    def get(self, request):
        print(request.user, request.auth)
        return Response({"status": True, "data": [11, 22, 33, 44]})

    def initialize_request(self, request, *args, **kwargs):
        # super().initialize_request()
        parser_context = self.get_parser_context(request)
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

    
# 配置文件
# ############## drf配置 ###############
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "ext.auth.QueryParamsAuthentication",
        "ext.auth.HeaderAuthentication",
        "ext.auth.NoAuthentication",
    ],
    "DEFAULT_THROTTLE_RATES": {
        "ip": "10/m",
        "user": "5/m"
    }
    # "DEFAULT_PERMISSION_CLASSES":[
    #     "ext.per.MyPermission"
    # ]

}
    

3、限流的缓存配置

限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。

  • 对于匿名用户,使用用户IP作为唯一标识。
  • 对于登录用户,使用用户ID或名称作为唯一标识
缓存={
	用户标识:[12:33,12:32,12:31,12:30,12,]    1小时/512:34   11:34
{

pip3 install django-redis
# settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "qwe123",
        }
    }
}

4、限流类的使用

from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('api/order/', views.OrderView.as_view()),
]


# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache


class ThrottledException(exceptions.APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_code = 'throttled'


class MyRateThrottle(SimpleRateThrottle):
    cache = default_cache  # 访问记录存放在django的缓存中(需设置缓存)
    scope = "user"  # 构造缓存中的key
    cache_format = 'throttle_%(scope)s_%(ident)s'

    # 设置访问频率,例如:1分钟允许访问10次
    # 其他:'s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day'
    THROTTLE_RATES = {"user": "10/m"}

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)

        # throttle_u # throttle_user_11.11.11.11ser_2

        return self.cache_format % {'scope': self.scope, 'ident': ident}

    def throttle_failure(self):
        wait = self.wait()
        detail = {
            "code": 1005,
            "data": "访问频率限制",
            'detail': "需等待{}s才能访问".format(int(wait))
        }
        raise ThrottledException(detail)


class OrderView(APIView):
    throttle_classes = [MyRateThrottle, ]

    def get(self, request):
        return Response({"code": 0, "data": "数据..."})


多个限流类

本质,每个限流的类中都有一个 allow_request 方法,此方法内部可以有三种情况:

  • 返回True,表示当前限流类允许访问,继续执行后续的限流类。
  • 返回False,表示当前限流类不允许访问,继续执行后续的限流类。所有的限流类执行完毕后,读取所有不允许的限流,并计算还需等待的时间。
  • 抛出异常,表示当前限流类不允许访问,后续限流类不再执行。

全局配置

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES":["xxx.xxx.xx.限流类", ],
    "DEFAULT_THROTTLE_RATES": {
        "user": "10/m",
        "xx":"100/h"
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值