限流就是限制用户的访问频率,如每分钟的操作的次数不超过3次等
1.
DRF中维护了一个缓存,以用户的唯一标识作为键,一个列表为值,存放着当前用户访问的时间戳。
当用户再次请求的时候,会根据用户唯一标识去缓存中找到列表,判断用户的访问次数是否超过限制,超出限制则禁止访问
在实际开发中,最常用的还是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": '' } } }
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)
-
返回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': '订单数据' })
REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES":["xxxx.xxxx.xx.类名","xxxx.xxxx.xx.类名",], 'DEFAULT_THROTTLE_RATES':{'user':'10/m'} }