一、节流

1、简介

节流又叫限流,限制访问。就是通常一个用户在多次请求一个页面,或者点击一个链接的时候,前几次点击是没有问题的,但是一旦连续几次之后,就会出现访问受限,离下一次访问还有50秒等的字样,在django rest framework 中有一个专门的组件来做限制访问。

2、思路

一旦一个用户向资源发送请求,那么根据用户的身份就有两种情况,匿名用户和认证用户。那么根据用户的身份怎么做限制访问?就是要找到用户的唯一标识。

  • 匿名用户:对于匿名用户,唯一能用来标识的只有请求的IP
  • 认证用户:认证用户的用户名,或者用户ID等。

用户标识的问题解决了,假设设置的是每分钟只能访问5次,也就是5次/min。当用户发送请求,可以拿到用户的唯一标识,判断用户是第几次访问。有下面几种情况:

  • 第一到五次:这是可以通过的,返回资源。

    一分钟之内

    • 第六次:请求被禁止,并返回提示信息。

      一分钟之后
    • 第六次:请求别允许,返回资源。

根据上面的情况可以得出以下思路:

当一个用户发送请求的时候,我可以在缓存(django rest framework就是这么做的)中生成一个字典,字典的键值对分别是用户的唯一标识和用户的访问时间,例如下面:

VISIT_RECORD = {
'weilan': [127,125, 121,110,89,68] # 标识第一次访问时间是68秒,第二次访问时间是89秒,第三次访问时间是110秒
}

第一步:当一个用户第一次发送请求的时候,缓存VISIT_RECORD中没有他的键,就会添加一个键是他的表示,值是一个列表,列表中存放他的第一次访问时间为t1。

第二步:当再次发送请求的时候,会先在缓存VISIT_RECORD中找有没有他的键,如果没有,会返回第一步。如果有,取出列表,查看列表中的最后一次访问值T1,并与本次访问时间Tn比较,如果Tn-T1>60s,则将T1删除,如果Tn-T1<60s,则保留T1,因为要保证一分钟之内的访问次数。

第三步:判断当前列表中保存的时间的个数,如果小于5个,说明一分钟之内还没有访问5次,将但访问时间Tn插入到列表头。如果个数超过5个,则说明一分钟已经访问过5次,本次访问已经是第6次,则不插人列表。

这样根据思路就可以写出下面限流类:

import time
VISIT_RECORD = {} # 这里再内存中生成,可以写入缓存
class VisitThrottle(BaseThrottle): def __init__(self):
self.history = None def allow_request(self,request,view):
# 1. 获取用户IP 或者认证用户的用户名
remote_addr = self.get_ident(request) 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) < 5:
history.insert(0,ctime)
return True # return True # 表示可以继续访问
# return False # 表示访问频率太高,被限制 def wait(self):
# 还需要等多少秒才能访问
ctime = time.time()
return 60 - (ctime - self.history[-1])

二、示例

1、目录结构

同样的我们向认证,权限那样再utils包中定义限流组件

2、具体限制访问

对于匿名用户和认证用户做不同的限制访问

from rest_framework.throttling import BaseThrottle,SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
scope = "anonymous" def get_cache_key(self, request, view):
return self.get_ident(request) class UserThrottle(SimpleRateThrottle):
scope = "user" def get_cache_key(self, request, view):
return request.user.username

3、配置限流类

可以再setting.py文件中全局配置,也可以再视图中重写,局部配置,但是访问频率,需要限流类的scope属性定义。

对于匿名用户,每分钟访问5次,认证用户,每分钟5次

REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
"DEFAULT_THROTTLE_RATES":{
"anonymous":'5/m',
"user":'10/m',
}
}

4、视图

from rest_framework.views import APIView

class UserInfoView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = [throttle.VisitThrottle] # 标识匿名用户访问 def get(self, request, *args, **kwargs):
print(request.META.get('REMOTE_ADDR')) # 这里可以获取到访问的IP
return HttpResponse('访问成功')

5、请求测试

使用postman或者浏览器发送请求

一分钟连续发送5次,正常



发送第6次时,访问受限

三、源码分析

django rest framework 之 认证一样进入,request的请求流程,进入源码查看具体权限的操作

1、进入dispath()方法

2、进入initial()方法

3、进入check_throttles()方法

4、获取限流类

获取限流类之后并实例化成对象,使得可以调用具体的方法

同样的默认的是通过全局配置

5、原生的限流类

rest framework中也有相应的限流类,主要使用SimpleRateThrottle,因为在SimpleRateThrottle中的一些方法已经是实现了我们需要的逻辑

来看一下SimpleRateThrottle具体做了什么

class SimpleRateThrottle(BaseThrottle):
cache = default_cache # default_cache其实是缓存的一个对象
timer = time.time
cache_format = 'throttle_%(scope)s_%(ident)s'
scope = None
THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES def __init__(self):
if not getattr(self, 'rate', None):
self.rate = self.get_rate()
self.num_requests, self.duration = self.parse_rate(self.rate) def get_cache_key(self, request, view): # 需要返回请求发情用户的唯一标识 raise NotImplementedError('.get_cache_key() must be overridden') def get_rate(self): # scope属性需要在节流类和配置文件中定义,才能达到节流的效果 if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg) try:
return self.THROTTLE_RATES[self.scope]
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg) def parse_rate(self, rate): # 解析配置文件中的时间等
if rate is None:
return (None, None)
num, period = rate.split('/')
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration) def allow_request(self, request, view): # 这与上面节流的操作相似,是具体的逻辑
if self.rate is None:
return True self.key = self.get_cache_key(request, view)
if self.key is None:
return True self.history = self.cache.get(self.key, [])
self.now = self.timer() while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success() def throttle_success(self):
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True def throttle_failure(self): # 请求失败的时候
return False 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)

以上就是节流的流程和源码分析

四、总结

节流同样可以通过全局配置和局部配置的方法,影响视图。

值得注意的是,有一个必须要重写的接口get_cache_key()

  • 当匿名用户的时候,返回值是匿名用户的IP
  • 当为认证用户的时候,可以是用户的任何唯一标识。

    因为在VISIT_RECORD中的键是唯一的。

scope定义了具体一个节流类怎么节流,在setting.py文件和节流类中都需要定义。SimpleRateThrottle中的parse_rate()方法对scope进行了解析

  • "user":'1/s', 表示一秒访问一次
  • "user":'1/m', 表示一分钟访问一次
  • "user":'1/h', 表示一小时访问一次
  • "user":'1/d', 表示一天访问一次

Django Rest framework 之 节流的更多相关文章

  1. Django Rest framework 之 序列化

    RESTful 规范 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) ...

  2. Django Rest framework 之 解析器

    RESTful 规范 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) ...

  3. Django Rest framework 之 版本

    RESTful 规范 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) ...

  4. Django Rest framework 之 权限

    django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) django res ...

  5. Django Rest framework 之 认证

    django rest framework 官网 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest fra ...

  6. Django Rest framework 之 视图

    RESTful 规范 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) ...

  7. Django Rest framework 之 分页

    RESTful 规范 django rest framework 之 认证(一) django rest framework 之 权限(二) django rest framework 之 节流(三) ...

  8. Django rest framework源码分析(3)----节流

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  9. Django rest framework(3)----节流

    目录 Django组件库之(一) APIView源码 Django restframework (1) ----认证 Django rest framework(2)----权限 Django res ...

随机推荐

  1. WPF 显示 mp3 专辑图片

    mp3 专辑图片是属于 mp3 的 tag 的一部分,安装 taglib# 的 nuget 安装包到项目.这里使用 TagLib.Portable https://www.nuget.org/pack ...

  2. DBUtils的增删改查

    数据准备: CREATE DATABASE mybase; USE mybase; CREATE TABLE users( uid INT PRIMARY KEY AUTO_INCREMENT, us ...

  3. 一种基于python的人脸识别开源系统

    今天在搜索人脸识别的文章时,无意中搜到一个比较开源代码,介绍说是这个系统人脸的识别率 是比较高的,可以达到:99.38%.这么高的识别率,着实把我吓了一跳.抱着实事求是的态度.个人 就做了一些验证和研 ...

  4. 人生苦短之---认识Python

    认识 Python 人生苦短,我用 Python —— Life is short, you need Python 目标 Python 的起源 为什么要用 Python? Python 的特点 Py ...

  5. Using SQLite database in your Windows 10 apps

    MVP可以在channel 9上传视频了,所以准备做个英文视频传上去分享给大家,本文做稿子. Hello everyone, As we all know, SQLite is a great and ...

  6. redis pipeline 独占链接

    pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭:如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作 ...

  7. jsp-9大内置对象简介

    产生背景 JSP引擎在调用JSP对应的jspServlet时,会传递或创建9个与web开发相关的对象供jspServlet使用.JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引 ...

  8. [原创]Struts2奇葩环境任意文件上传工具(解决菜刀无法传文件或上传乱码等问题)

    上面这问题问得好  1 不知道大家有没碰到有些Strus2站点  上传JSP后访问404 或者503    注意我说的是404或503不是403(要是403换个css/img等目录或许可以)    但 ...

  9. JavaEmail-邮箱

    package service; import com.sun.mail.util.MailSSLSocketFactory;import javax.mail.Message;import java ...

  10. Linux学习笔记之七————Linux常用命令之编辑器、服务器

    <1>gedit编辑器 gedit是一个Linux环境下的文本编辑器,类似windows下的写字板程序,在不需要特别复杂的编程环境下,作为基本的文本编辑器比较合适.   <2> ...