restframework之节流
2024-10-21 06:42:02
基本思路(原生Django而言):
在django2.x中,若出现节流(访问频率控制)的需求,我们首先想到的是使用一个字典(dict类型)来存储所有IP地址的访问时间记录,这是针对于匿名用户(IP)而言的
{
"111.222.3.44": [, , , , ....],
"1.333.44.555": [, , , .....]
}
原生request的消息体中,在environ下的REMOTE_ADDR保存了客户端的IP地址
由于请求经过WSGIRequest封装后,必经中间件层,所以考虑通过中间件来实现节流功能
from django.utils.deprecation import MiddlewareMixin
from django.http.response import HttpResponse
import time MAX_PER_VISITED_NUMS = 3
VISITED_IP_THROTTLE = dict() class ThrottleMiddleware(MiddlewareMixin):
"""
节流中间件
VISITED_IP_THROTTLE每分钟最大访问次数
"""
def request_allowed(self, request):
"""自定义访问频率限制器, 用于限制匿名用户的访问频率限制"""
ip = request.META['REMOTE_ADDR']
cur_time = time.time()
# 如果ip在限制器中有记录
if ip in VISITED_IP_THROTTLE:
visiting_record = VISITED_IP_THROTTLE[ip] while visiting_record:
# 与列表中最后一次访问记录的时间差大于60秒, 移除最后一次记录并继续循环
if cur_time - 60 >= visiting_record[-1]:
visiting_record.pop(-1)
continue
# 访问记录列表超过最大访问次数
if len(visiting_record) >= MAX_PER_VISITED_NUMS:
return False
# 与列表中最后一次访问记录的时间差小于60秒
visiting_record.insert(0, cur_time)
return True # 无记录则新建该IP访问记录
VISITED_IP_THROTTLE[ip] = list()
VISITED_IP_THROTTLE[ip].insert(0, cur_time)
return True def process_request(self, request):
if self.request_allowed(request):
print(True)
return HttpResponse("True")
else:
print(False)
return HttpResponse("False")
运行结果:
这是针对在访问用户较少的情况下才采用的全局变量来存储访问记录, 实际情况中,最好使用redis作为缓存来存储记录。
restframework版:
自定义节流控制器
使用redis缓存访问记录(hash类型)
redispool.py(作连接池, 参数设置为你自己的redis连接信息)
import redis
from vue_shop import settings REDIS_POOL = redis.ConnectionPool(**settings.REDIS_KWARGS)
throttle.py
import time
import redis
from utils.redispool import REDIS_POOL
import json
from rest_framework.throttling import SimpleRateThrottle class AnonymityUserThrottle(object):
"""
自定义匿名用户节流器, 采用redis缓存访问记录
MAX_PER_VISITED_NUMS: 每分钟最高访问次数
name: 缓存(redis)中,hash类型的name
"""
MAX_PER_VISITED_NUMS = 3
name = "AnonymityVisitedRecord" def allow_request(self, request, view):
"""
进行是否可访问的逻辑判断
:param request: drf's request
:param view:
:return: bool
"""
IP_ADDR = request.META.get("REMOTE_ADDR", None)
cur_visit_time = time.time()
redis_conn = redis.Redis(connection_pool=REDIS_POOL)
ips_record = redis_conn.hget(self.name, IP_ADDR)
print(ips_record)
if ips_record:
history_visit_list = json.loads(ips_record.decode())
while history_visit_list:
# [120, 99, 40]
# 最远一次访问时间B与当前时间A的时间差大于60秒,则删除B,如此循环
if cur_visit_time - history_visit_list[-1] >= 60:
history_visit_list.pop(-1)
redis_conn.hset(self.name, IP_ADDR, json.dumps(history_visit_list))
continue
if len(history_visit_list) > self.MAX_PER_VISITED_NUMS:
return False
history_visit_list.insert(0, cur_visit_time)
redis_conn.hset(self.name, IP_ADDR, json.dumps(history_visit_list))
return True new_ip_record = list()
new_ip_record.insert(0, cur_visit_time)
redis_conn.hset(self.name, IP_ADDR, json.dumps(new_ip_record))
return True def wait(self):
"""
Optionally, return a recommended number of seconds to wait before
the next request.
"""
return None
运行结果:
内置的节流控制器
class SimpleAnonymityUsertThrottle(SimpleRateThrottle):
"""
使用restframework内置的节流类,针对于的匿名用户的访问控制
"""
scope = "Anonymity" def get_cache_key(self, request, view):
"""
返回用户的唯一标识
"""
# return request.META.get("REMOTE_ADDR")
return self.get_ident(request) class CertifiedUserThrottle(SimpleRateThrottle):
"""
对认证通过的用户进行节流控制
"""
scope = "CertifiedUser" def get_cache_key(self, request, view):
"""返回唯一用户标识"""
return request.user.username
scope参数依赖配置:
REST_FRAMEWORK =
{
'DEFAULT_THROTTLE_RATES': {
'Anonymity': '3/m',
'realUser': '6/m',
},
}
最新文章
- 工作记事 unknownHost
- 代码中AggregateException的处理
- Linux之ls命令
- 1CSS与文档
- PHPCMS列表页伪静态
- Notepad++前端开发常用插件介绍 - BorisHuai前端修炼 - 博客频道 - CSDN
- springside springmvc 的一个SB问题
- 基于libuv库的UDP收/发广播消息代码实现
- removeTask
- iOS正则表达式的使用
- 1740: [Usaco2005 mar]Yogurt factory 奶酪工厂
- A股魔咒
- 常用正则表达式 c#
- MySQL利用binlog恢复误操作数据(python脚本)
- ORACLE in与exists语句的区别
- C#:文件夹匹配
- python爬虫 ----文章爬虫(合理处理字符串中的\n\t\r........)
- Spring Boot 揭秘与实战(九) 应用监控篇 - 自定义监控端点
- DrawDib 使用例子<;转>;
- k8s 问题
热门文章
- InheritedWidget and screen
- vue mixins是什么及应用
- data:image/png;base64应用
- 基于 Express + MySQL + Redis 搭建多用户博客系统
- mysql修改字符集问题
- 【RocketMQ异常】Caused by: com.aliyun.openservices.shade.com.alibaba.rocketmq.client.exception.MQClientException: No route info of this topic, message-service-topic-testf
- linux系统信息获取和上报
- PHP编程实现阳历转换为阴历的方法
- C++(五十) — 容器中元素满足的条件
- spriingboot使用thymeleaf