流量控制的逻辑:
1、设置一个唯一标识,作为cache的key
2、请求访问时,构造唯一标识,从ceche中获取[时间1,时间2,时间3,…]
3、根据设置流量控制规则,如:5/m (一分钟最多访问5次),判断该唯一标识记录到cache中的访问时间的次数,是否满足请求的条件
4、不满足,就抛出异常,前端收到请求过快的响应。满足访问,就直接访问。
视图:
class TestAPIView(APIView):
authentication_classes=[MyJWTAuthentication]
permission_classes = [AdminPermission,]
def get(self,request)
return Respponse({'code':200,'msg':'测试通过'})
路由:
path('test/',views.TestAPIView.as_view())
请求方式:GET
流量控制类:
from rest_framework.throttling import SimpleRateThrottle
#对请求源的IP进行限制
class IPThrottle(SimpleRateThrottle):
# 在settings中配置频率时使用到的关键字,有scope来指定
scope = 'IP'
def get_cache_key(self, request, view):
return f'{self.scope}-{self.get_ident(request)}-throttle'
# 这里return什么,就以什么作为限制,这里限制ip,直接return ip就可以
return request.META.get('REMOTE_ADDR')
# 可以在这里设置访问频率
# def get_rate(self):
# return '3/m'
1、TestAPIView的as_view方法,当前实例对象没有,找父类的APIView的as_view
@classmethod
def as_view(cls, **initkwargs):
#1、调用APIView父类的as_view方法
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
#2、去除掉csrf校验
return csrf_exempt(view)
2、APIView的as_view是调用父类View的as_view方法
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
return view
#返回的是闭包view,
#view的功能就是self.dispatch()方法
3、as_view主要返回闭包,关于self.dispatch()方法
4、self是TestAPIView , 没有dispatch方法,调用父类APIView的dispatch方法
#简化后的源码
def dispatch(self, request, *args, **kwargs):
#处理url中的参数
self.args = args
self.kwargs = kwargs
#构建drf的request对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
#认证、权限、限流的执行
self.initial(request, *args, **kwargs)
#通过反射来执行TestAPIView中的get、post等方法
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
#视图函数执行结果
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
5、self.initial(request,),TestAPIView中没有,找APIView的initial方法,
def initial(self, request, *args, **kwargs):
#认证、权限、限流
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
6、TestAPIView没有check_throttles,调用APIView的check_throttles
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
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)
self.get_throttles(),TestAPIView没有,调用父类APIView的get_throttles()
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
具体流量控制了流程
7、SimpleRateThrottle的allow_request方法
class SimpleRateThrottle(BaseThrottle):
def __init__(self):
#获取限流的规则,如5/m , 没有就请求get_rate方法
if not getattr(self, 'rate', None):
self.rate = self.get_rate()
#解析限制规则5/m, 允许访问的次数,允许的时间(秒)
self.num_requests, self.duration = self.parse_rate(self.rate)
def allow_request(self, request, view):
if self.rate is None:
return True
#获取唯一标识,cache的key
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
#获取历史访问的列表[时间点1,时间点2]
self.history = self.cache.get(self.key, [])
self.now = self.timer()
#把历史列表中,超过1分钟的时间点移除(5/m)
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
#如果1分钟内访问的历史次数,大于等于规则5/m中的5,就是访问失败,抛出异常
if len(self.history) >= self.num_requests:
return self.throttle_failure()
#否则访问超过
return self.throttle_success()
8、回到APIView.check_throttles()
def check_throttles(self, request):
#记录要等待多少时间,才能请求
throttle_durations = []
for throttle in self.get_throttles():
#如果限流类返回False,就把要等待时间放到throttle_durations列表中
if not throttle.allow_request(request, self):
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)
9、限流类.wait() 方法
def wait(self):
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)
10、回到APIView.dispatch方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
#认证、权限、限流
self.initial(request, *args, **kwargs)
#反射TestAPIView,获取get方法,执行get方法,
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response