Django drf限流
Django drf限流
16.1 全局配置
配置文件settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名用户,未登录的
'rest_framework.throttling.UserRateThrottle' # 经过登录之后的用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '2/minute',
'user': '5/minute',
}
}
视图
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
class ExampleView2(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
def get(self, request):
print(request.user.id)
return Response({"message": "您是已登录用户,欢迎您使用高级功能"})
路由
from django.urls import path
from . import views
urlpatterns = [
path('throt/', views.ExampleView2.as_view()),
]
访问效果
未登录用户第3次访问时:
{
"detail": "请求超过了限速。 Expected available in 56 seconds."
}
已登录用户第6次访问的时候:
HTTP 429 Too Many Requests
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Retry-After: 55
Vary: Accept
{
"detail": "请求超过了限速。 Expected available in 55 seconds."
}
16.2 局部配置
配置文件settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '2/minute', # 未登录用户每分钟访问2次
'user': '5/minute', # 登录用户每分钟访问5次
}
}
视图
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
class ExampleView3(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
throttle_classes = [UserRateThrottle, AnonRateThrottle]
def get(self, request):
print(request.user.id)
return Response({"message": "您是已登录用户,欢迎您使用高级功能"})
路由
from django.urls import path
from . import views
urlpatterns = [
path('throt2/', views.ExampleView3.as_view()),
]
访问效果
未登录用户第3次访问ExampleView3时:
{
"detail": "请求超过了限速。 Expected available in 58 seconds."
}
已登录用户第6次访问ExampleView3的时候:
HTTP 429 Too Many Requests
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Retry-After: 56
Vary: Accept
{
"detail": "请求超过了限速。 Expected available in 56 seconds."
}
16.3 可选限流类
(1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
(2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
(3)ScopedRateThrottle (待定…)
限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。
ScopedRateThrottle的使用:
在settings.py配置文件中配置如下:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
# 指定的视图类中设置:throttle_scope = 'contacts',即表示此接口每分钟最多访问10次
'contacts': '10/m',
# 指定的视图类中设置:throttle_scope = 'uploads',即表示此接口每分钟最多访问5次
'uploads': '5/m'
}
}
例如:
from rest_framework.views import APIView
class ContactListView(APIView):
# 如此设置即表明此接口每分钟最多访问10次
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
# 如此设置即表明此接口每分钟最多访问10次
throttle_scope = 'contacts'
...
class UploadView(APIView):
# 如此设置即表明此接口每分钟最多访问5次
throttle_scope = 'uploads'
...
16.4 自定义限流组件
VISIT_RECORD = {} #定义全局变量,用于存放访问记录
class VisitThrottle(object):
def __init__(self): #用于await计算剩余访问时间
self.history = None
def allow_request(self,request,view):
#获取用户ip作为唯一的标示
remote_addr = request.META.get('REMOTE_ADDR')
# 获取当前访问的时刻
ctime = time.time()
# 这是用户第一次访问,将其进行记录,并且返回True,允许继续访问
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
# 如果不是第一次访问,获取所有的记录
history = VISIT_RECORD.get(remote_addr)
self.history = history
# 判断最开始的时刻与现在的时刻的差值是否在规定的时间范围内,比如在60s内,如果不在,
# 可以去除最开始的时刻记录
while history and history[-1] < ctime - 30:
history.pop()
# 此时列表中的时刻记录都是在规定的时间范围内,判断时刻的个数也就是访问的次数
if len(history) < 3:
history.insert(0,ctime)
return True
def wait(self):
# 还需要等多少秒才能访问
ctime = time.time()
return 60 - (ctime - self.history[-1])
在对应的视图中进行配置:
class BookView(ListAPIView):
throttle_classes = [VisitThrottle,] #配置限流组件
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
限流的原理
在rest framework框架中,限流定义为类的列表,只需要全局配置或者局部配置即可。上述限流的原理就是以客户端的唯一标示作为键,以访问的时刻形成的列表作为值形成的字典,然后通过对字典进行操作:
{
http://127.0.0.1:8020/ :[11:43:30,11:42:22,11:42:20,11:42:09]
}
如上面的字典所示,后面的访问时间放插入到列表的最左侧,加入当前访问时间是11:43::30,那么与最开始访问时间11:42:09进行做差,然后与规定时间30s进行比较,如果不在30s内,那么就去除最左边的记录,同理使用while循环依次比较,最后在规定时间范围内的记录:
{
http://127.0.0.1:8020/ :[11:43:30,]
}
再计算访问次数,也就是列表的个数,显然如果列表的个数小于3可以继续访问,否则不可以。
上面使用全局变量来进行记录,当然也是可以使用缓存来进行记录的存储,需要使用django的缓存API,from django.core.cache import cache,导入这个API后就可以使用set和get方法,设置和获取cache中存储的对象,只需要在操作全局变量除进行替换即可:
from django.core.cache import cache as default_cache
import time
class VisitThrottle(object):
cache = default_cache
def allow_request(self,request,view):
...
...
# 这是用户第一次访问,将其进行记录,并且返回True,允许继续访问
if not self.cache.get(remote_addr):
self.cache.set(remote_addr,[ctime,])
return True
# 如果不是第一次访问,获取所有的记录
history = self.cache.get(remote_addr)
self.history = history
...
...
rest framework的限流组件就是基于cache来完成的
上述的wait方法表示还需要多长时间可以进行访问这个API,对客户端的提示:
{
"detail": "Request was throttled. Expected available in 56 seconds."
}