限流就是限制用户的访问频率,如每分钟的操作的次数不超过3次等

  • 对于匿名用户,使用用户IP作为唯一标识

  • 对于登录用户,则使用用户的ID作为唯一标识

1.限流的机制

DRF中维护了一个缓存,以用户的唯一标识作为键,一个列表为值,存放着当前用户访问的时间戳。
当用户再次请求的时候,会根据用户唯一标识去缓存中找到列表,判断用户的访问次数是否超过限制,超出限制则禁止访问

在实际开发中,最常用的还是redis

2.配置redis

在setting.py中配置缓存为redis

pip install django-redis
CASHES = {
    "default": {
        'BACKEND': "django_redis.cache.RedisCache",
        'LOCATION': 'redis"//127.0.0.1:6379',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            "PASSWORD": ''
        }
    }
}

 

3.限流类的使用

THROTTLE_RATES = {'user': '10/m'}
# s, m, h, d 秒 分 时 天

定义限流类

 1 from rest_framework.views import APIView
 2 from rest_framework.response import Response
 3 from rest_framework.authentication import BaseAuthentication
 4 from rest_framework.exceptions import AuthenticationFailed, APIException
 5 from rest_framework.permissions import BasePermission
 6 from rest_framework.throttling import SimpleRateThrottle
 7 from django.core.cache import cache as default_cache
 8 from app01.models import UserInfo
 9 from rest_framework import status
10 
11 class ThrottleException(APIException):
12     status_code = status.HTTP_429_TOO_MANY_REQUESTS
13     default_code = 'throttled'
14 
15 
16 class MyThrottle(SimpleRateThrottle):
17     cache = default_cache  # django中的缓存
18     scope = 'user'  # 用于构造缓存中的key
19     cache_format = 'throttle_%(scope)s_%(ident)s'
20 
21     # 限制配置
22     THROTTLE_RATES = {'user': '10/m'}
23 
24     def get_cache_key(self, request, view):
25         """获取用户的唯一标识"""
26         if request.user:
27             ident = request.user.pk
28         else:
29             ident = self.get_ident(request)  # 获取匿名用户的Ip
30         return self.cache_format % {'scope': self.scope, 'ident': ident}
31 
32     def throttle_failure(self):
33       # 获取下次可以访问的时间间隔
34         wait = self.wait()
35         detail = {
36             'code': 1005,
37             'data': '访问频率限制',
38             'detail': '需要等待{}s'.format(int(wait))
39         }
40         raise ThrottleException(detail)

视图类

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

    def get(self, request, *args, **kwargs):
        return Response({
            'code': 0,
            'data': '订单数据'
        })

 

 如果我么希望设置5分钟每次,就可以重写如下方法

class NewsCreateRateThrottle(SimpleRateThrottle):
    cache = default_cache
    scope = "user"
    cache_format = 'throttle_%(scope)s_%(ident)s'

    THROTTLE_RATES = {'user': "1/5m"}

    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[-1]]
        count = int(period[0:-1])
        return (num_requests, duration * count)

 

4.多个限流类的使用

我们是可以配置多个限流类的,按照定义顺序逐个执行,执行的结果有以下三种

  • 返回True: 表示限流校验成功,可以继续访问,但会继续执行下一个限流类

  • 返回False: 表示限流校验失败,执行下一个限流类

  • 抛出异常,不会执行下一个限流类了

如果所有的限流类都没有抛出异常,我们又想自定义超出限制的提示信息,就需要在视图类中自定义**`throttled`**方法

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 ThrottleException(APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_code = 'throttled'

    
    
class MyThrottle(SimpleRateThrottle):
    cache = default_cache  # django中的缓存
    scope = 'user'  # 用于构造缓存中的key
    cache_format = 'throttle_%(scope)s_%(ident)s'

    # 限制配置
    THROTTLE_RATES = {'user': '10/m'}

    def get_cache_key(self, request, view):
        """获取用户的唯一标识"""
        if request.user:
            ident = request.user.pk
        else:
            ident = self.get_ident(request)  # 获取匿名用户的Ip
        return self.cache_format % {'scope': self.scope, 'ident': ident}

    def throttle_failure(self):
        return False
   
  
class OrderView(APIView):
    throttle_classes = [MyThrottle, ]

    def throttled(self, request, wait):
        detail = {
            'code': 1005,
            'data': '访问频率限制',
            'detail': '需要等待{}s'.format(int(wait))
        }
        raise ThrottleException(detail)

    def get(self, request, *args, **kwargs):
        return Response({
            'code': 0,
            'data': '订单数据'
        })

 

5.全局配置

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",],
    'DEFAULT_THROTTLE_RATES':{'user':'10/m'}
}

 

6.源码分析

 

posted on 2022-11-15 10:57  阿明明  阅读(102)  评论(0编辑  收藏  举报