DRF之频率
一. DRF频率
开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用。
1. 频率组件原理
DRF中的频率控制基本原理是基于访问次数和时间的,当然我们可以通过自己定义的方法来实现。
当我们请求进来,走到我们频率组件的时候,DRF内部会有一个字典来记录访问者的IP,
以这个访问者的IP为key,value为一个列表,存放访问者每次访问的时间,
{ IP1: [第三次访问时间,第二次访问时间,第一次访问时间],}
把每次访问最新时间放入列表的最前面,记录这样一个数据结构后,通过什么方式限流呢~~
如果我们设置的是10秒内只能访问5次,
-- 1,判断访问者的IP是否在这个请求IP的字典里
-- 2,保证这个列表里都是最近10秒内的访问的时间
判断当前请求时间和列表里最早的(也就是最后的)请求时间的查
如果差大于10秒,说明请求以及不是最近10秒内的,删除掉,
继续判断倒数第二个,直到差值小于10秒
-- 3,判断列表的长度(即访问次数),是否大于我们设置的5次,
如果大于就限流,否则放行,并把时间放入列表的最前面。
2. 简单应用
自定义频率类,自己实现通过IP来限制访问频率:
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle import time from rest_framework import exceptions visit_record = {} class VisitThrottle(BaseThrottle): # 限制访问时间 VISIT_TIME = 10 VISIT_COUNT = 3 # 定义方法 方法名和参数不能变 def allow_request(self, request, view): # 获取登录主机的id id = request.META.get('REMOTE_ADDR') self.now = time.time() if id not in visit_record: visit_record[id] = [] self.history = visit_record[id] # 限制访问时间 while self.history and self.now - self.history[-1] > self.VISIT_TIME: self.history.pop() # 此时 history中只保存了最近10秒钟的访问记录 if len(self.history) >= self.VISIT_COUNT: return False else: self.history.insert(0, self.now) return True def wait(self): return self.history[-1] + self.VISIT_TIME - self.now
如果是以IP地址为key来限制流量的话,用DRF给我们提供的 SimpleRateThrottle 就可以做到了。
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope="visit_rate" def get_cache_key(self, request, view): return self.get_ident(request)
特别注意:
1.这个get_cache_key必须返回一个key,这个key默认用return self.get_ident(request)可以获得IP地址,就是以IP地址为key做限流。但是有些时候,需要用username做限制,就可以返回request.user.username。然后再全局配置,根据需求,假如除了登录用IP限制,其他业务视图用用户名限制,就可以这么配置。
只需要在登录视图加一个IP限制的类就可以了:
2.这个scope代表你的频率方法,在settings中配置:
全局使用
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", } }
局部使用
class TestView(APIView): authentication_classes = [MyAuth,] permission_classes = [MyPermission, ] throttle_classes = [VisitThrottle, ] def get(self, request): print(request.user) print(request.auth) # user_id = request.user.id return Response("认证测试")