Django DRF之限流
这个玩意怎么说呢,其实之前我没有太重视,因为我觉得本身反爬就是博弈的过程,对面完全可以用代理IP、批量注册等方式实现抓取想要的内容。直到最近我发现我错了。在公司内部的一个团队使用的小项目,总是机器疯狂报实例100%使用。每次排查都发现接口被几个IP疯狂的访问,通过排查还是公司内部有安全部门的检测,但这种检测对于一些内部服务来说存在访问频率太高,造成内部的一些小机器服务根本扛不住的问题,所以这不,节流来了。
实现原理
既然都已经看源码了,那就总结一下drf
不亏是非常有开发经验的人开发出来的,基本能想到的、想不到的都帮我们实现了。
自己实现版
import time
from rest_framework.throttling import BaseThrottle
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
def __init__(self):
self.history = None
def allow_request(self, request, view):
# 1、获取IP,self.get_ident为BaseThrottle类提供的方法
remote_addr = self.get_ident(request)
# 2、保存
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60:
history.pop()
if len(history) < 3:
history.insert(0, ctime)
return True
def wait(self):
# 提示
ctime = time.time()
return 60 - (ctime - self.history[-1])
这个原理还是比较简单的,主要就是allow_request
、wait
2个方法的实现。
流程
通过看对应的源码,他的流程还是先从请求进来后走self.dispatch()
、self.initial()
、self.check_throttles()
这个流程。(是在认证、权限判断之后)
配置及使用
这步还是比较简单的,直接在settings.py
继续配置即可。
# 修改DRF认证
REST_FRAMEWORK = {
...
# 节流配置
"DEFAULT_THROTTLE_CLASSES": [
'utils.throttle.VisitThrottle'
]
}
但是我们实际使用中,肯定不会用上面的自己实现版本来做,因为drf
已经提供了一个更nb的方法给到我们可以简单使用。SimpleRateThrottle
方法。由于实现的已经非常完善了,我们甚至只要几行代码就可以轻松实现。具体逻辑如上,实现也比较清晰。
# utils.throttle.VisitThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "endpein"
def get_cache_key(self, request, view):
# 需要实现告知以什么来作为唯一身份判断
return self.get_ident(request)
# settings
REST_FRAMEWORK = {
...
# 节流配置
"DEFAULT_THROTTLE_CLASSES": [
'utils.throttle.VisitThrottle'
],
"DEFAULT_THROTTLE_RATES": {
# 配置每分钟只能访问3次
"endpein": '3/m'
}
通过查看源码,支持的限流配置时间格式如下
{'s':1,'m':60,'h':3600,'d':86400}
同时可以针对登陆用户和非登陆用户进行不同的限制,所以在配置上会有所不同。但是非常简单了。
# utils.throttle.VisitThrottle
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "endpein"
def get_cache_key(self, request, view):
# 需要实现告知以什么来作为唯一身份判断
return self.get_ident(request)
# 登陆用户
class UserThrottle(SimpleRateThrottle):
scope = "endpeinUser"
def get_cache_key(self, request, view):
# 注意,这里有区别了,返回的是用户信息
return request.user.username
# settings
REST_FRAMEWORK = {
...
# 节流配置
"DEFAULT_THROTTLE_CLASSES": [
'utils.throttle.VisitThrottle',
],
"DEFAULT_THROTTLE_RATES": {
# 配置每分钟只能访问3次
"endpein": '3/m',
"endpeinUser":'10/m'
}
注意,在上面代码,我并没有在DEFAULT_THROTTLE_CLASSES
中增加UserThrottle
类,而是对于非通用的,最简单的方式还是去对应的视图类下 增加throttle_classes = []
这种方式来进行指定使用。