建议先看完本人的另两篇博客:认证(http://t.csdnimg.cn/G4idK)和权限(http://t.csdnimg.cn/0hpJf),或者了解认证和权限,有一定基础,再来看本篇博客。
基本原理
开发过程中如果某个接口不想让用户频繁访问,那么就需要限流。
对于已登陆的用户,用户信息的唯一主键作为唯一标识。对于未登陆的用户,IP作为唯一标识。
每个唯一标识都在redis中(或者Django默认缓存中)对应着其访问记录的列表。譬如IP为abcd
的用户,在22:40
,21:10
时分别访问了该接口。那么在redis中(或者Django默认缓存中)便维护着"abcd": [22:40, 21:10]
的记录。
譬如现在想要限制频率为10分钟只能访问三次,那么步骤如下:
- 获取当前时间
- 当前时间-10分钟=计数开始时间
- 根据唯一标识获取到访问记录
- 在访问列表中删除小于计数开始时间的记录
- 计算现在访问列表
- 长度达到3,抛出异常
- 长度未超过3,访问该接口
限流组件(即限流器)
在tools中新建throttle.py,代码如下:
from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
# 构造缓存中的key
scope = "user"
# 设置访问频率
THROTTLE_RATES = {"user": "3/m"}
def get_cache_key(self, request, view):
if request.user:
# 存储在数据库里的用户id,因为id被我们设置成了主键
identification = request.user.pk
else:
# 去请求里获取用户的IP地址
identification = self.get_ident(request)
return self.cache_format % {
'scope': self.scope,
'ident': identification
}
可以发现不同于认证和权限,其组件都是继承自BaseXXX,一般自定义的限流组件都是继承自SimpleRateThrottle这种具体实现子类。
如果是SimpleRateThrottle的话,就需要设置scope和THROTTLE_RATES,分别对应着redis中的唯一标识,和访问限制频率。然后重写get_cache_key方法。
在如上示例我们的get_cache_key方法主要逻辑就是,对于已经登陆的用户,拿到用户主键作为唯一标识。对于未登陆的用户拿到IP地址作为唯一标识。然后传给SimpleRateThrottle的cache_format方法,这个方法里面会根据唯一标识和访问限制频率自动判断是否需要限流,如果需要限流还会返回还需要等待多少秒。
限流方案
单视图设置
views.py
class LoginView(APIView):
throttle_classes = [MyThrottle, ]
然后发现前三次都可以正常访问,但是第四次就会出现如下:
全局设置
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"app01.tools.throttle.MyThrottle"
]
}
同时注意注释掉单视图的相关throttle_classes那一行,然后测试发现一样是第四次之后就被拒绝访问。
而且需要注意的是,全局设置还可以设置访问限制频率。
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"app01.tools.throttle.MyThrottle"
],
"DEFAULT_THROTTLE_RATES": {"user": "3/m"}
}
再把限流组件中的THROTTLE_RATES注释掉,然后测试发现一样是第四次之后就被拒绝访问。
源码分析