DRF内置频率组件Throttling
可以对接口访问的频次进行限制,以减轻服务器压力。
一般用于付费购买次数,投票等场景使用.
可选限流级别
1) AnonRateThrottle
-
限制所有匿名未认证用户,使用IP区分用户。
-
使用
DEFAULT_THROTTLE_RATES['anon']
来设置频次
2)UserRateThrottle
-
限制认证用户,使用User id 来区分。
-
使用
DEFAULT_THROTTLE_RATES['user']
来设置频次
全局配置
可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES
和 DEFAULT_THROTTLE_RATES
进行全局配置
REST_FRAMEWORK = {
# 可选限流级别
'DEFAULT_THROTTLE_CLASSES': (
# 限制所有匿名未认证用户,使用IP区分用户
'rest_framework.throttling.AnonRateThrottle',
# 限制经过认证后的用户,使用User_id 来区分
'rest_framework.throttling.UserRateThrottle'
),
# 设置访问频率
'DEFAULT_THROTTLE_RATES': {
# 匿名未认证用户访问所有接口一分钟最多访问3次
'anon': '3/m',
# 经过认证后的用户访问所有接口一分钟最多访问10次
'user': '10/m'
}
}
DEFAULT_THROTTLE_RATES
可以使用second
,minute
,hour
或day
来指明周期。
源码:
{'s': 1, 'm': 60, 'h': 3600, 'd': 86400} m表示分钟,可以写m,也可以写minute
局部配置
也可以在具体视图中通过 throttle_classess
属性来配置,如:
from rest_framework.views import APIView
from rest_framework.throttling import AnonRateThrottle
from rest_framework.throttling import UserRateThrottle
class ExampleView1(APIView):
# 即未经过认证的用户访问此接口最多每分钟访问3次
# 即经过认证后的用户访问此接口最多每分钟访问10次
throttle_classes = (AnonRateThrottle, UserRateThrottle)
...
# settings.py配置文件中设置针对经过认证的用户和未经过认证的用户的限制频次
REST_FRAMEWORK = {
# 设置访问频率
'DEFAULT_THROTTLE_RATES': {
# 匿名未认证用户访问所有接口一分钟最多访问3次
'anon': '3/m',
# 经过认证后的用户访问所有接口一分钟最多访问10次
'user': '10/m'
}
}
ScopedRateThrottle
DRF还提供了一种限流级别——>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'
...
自定义限流
限流组件又叫做频率组件,用于控制客户端可以对API进行的请求频率,比如说1分钟访问3次,如果在1分钟内超过3次就对客户端进行限制。
假设现在对一个API访问,在30s内访问不能超过3次,应该如何实现?
自定义限流组件
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."
}