django_restfreamwork 6 限流组件

Posted on 2020-08-22 16:08  王石头py  阅读(302)  评论(0)    收藏  举报

DRF【限流组件】

作用和原理

作用:开放平台API接口的调用需要限制其频率,节约服务器避免恶意频繁调用

正常用户,一分钟访问3-5次,如果是脚本恶意可能一分钟访问10次以上

自定义限流策略

举例:以IP地址作为限流(最常用)

构建访问列表

需要构造访问列表 key为ip,value则为[访问时间1,访问时间2,访问时间3]

限流策略

  • 1,获取ip地址
  • 2,判断ip地址是否在IP的访问列表中
    • 如果不在访问列表中,将这个ip添加到访问列表中,添加一条记录: ip:[访问的时间1,]
    • 如果在访问列表中(之前访问过),将这个ip当前访问的时间加入到value的列表中: ip:[访问时间1,访问时间2]
  • 3,时间判断:确保列表中这个ip的 最新的访问时间 最老的访问时间 之间的时间差是 自定义时间之内(比如1分钟之内)
  • 4,次数判断:得到列表的长度,判断是否是允许的次数,判断某段时间内,访问次数超过多少次,做限流

新建 throttle.py ,也可以新建在utils/throttle.py 中

throttle.py 作为自定义限流组件

比如策略为 60秒之内,同一个IP请求超过3次,做限流

from rest_framework.throttling import BaseThrottle
import time

VISIT_RECORD = {}   #默认访问列表

class MyThrottle(BaseThrottle):

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        """
            实现限流的逻辑
            以IP限流(最常用)
            访问列表 key为ip,value则为[访问时间1,访问时间2,访问时间3]
                {IP: [访问时间1, 访问时间2, 访问时间3]}
        """
        # 1, 获取请求的IP地址
        ip = request.META.get("REMOTE_ADDR")
        # 2,判断IP地址是否在访问列表
        now = time.time()
        if ip not in VISIT_RECORD:  #如果不在访问列表中,证明第一次访问
            VISIT_RECORD[ip] = [now,]
            # 不限流直接return True
            return True
        # 如果在访问列表,则先拿出这个ip的历史记录
        history = VISIT_RECORD[ip]
        history.insert(0, now)  #插入列表最前面0号角标,也可以插入到最后面-1
        # 3,时间频率:确保列表里最新访问时间以及最老的访问时间差 是1分钟(60秒)
        while history and history[0] - history[-1] > 60:
            history.pop()
        self.history = history
        # 4,次数频率:得到列表长度,判断是否是允许的次数
        if len(history) > 3:
            """60秒之内同一个IP请求3次以上,做限流"""
            return False
        else:
            return True

    def wait(self):
        """
            返回需要再等多久才能访问
        """
        time = 60 - (self.history[0] - self.history[-1])
        return time

对指定接口做限流策略

views.py

# from .models import Book
# from rest_framework.views import APIView
# from rest_framework.response import Response
# from .serializers import BookSerializer
# from rest_framework.permissions import IsAuthenticated
from .throttle import MyThrottle        #导入自定义限流类
# class BookView(APIView):
			throttle_classes = [MyThrottle]     #对这个接口 自定义限流策略

#     def get(self, request):
#         book_list = Book.objects.all()
#         ret = BookSerializer(book_list, many=True)
#         return Response(ret.data)

序列化器忽略

测试访问限流策略

60秒内前三次刷新页面都可以获取数据,第4次开始接口返回了限流提示

框架默认限流策略

重写get_catch_key

新建 throttle.py ,也可以新建在utils/throttle.py 中

throttle.py 作为框架限流组件

类继承SimpleRateThrottle 并重写 get_cache_key

from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
    """
        scope的值要和settings中配置的值要相同才会生效
    """
    scope = "aaa"   #限流策略名

    def get_cache_key(self, request, view):
        """
            需要重写 get_cache_key ,因为父类的 get_cache_key 会抛异常
            重写只需返回什么(比如ip),就按照什么策略进行限流
        """
        key = self.get_ident(request)   #框架中 self.get_ident会获取ip地址
        return key

配置访问频率

settings.py中新增配置

settings中key访问策略名 aaa 和配置中频率的 key aaa 要一样,才会生效

#REST_FRAMEWORK = {
    #jwt登录认证
   	....
  	....
    # 频率组件
    "DEFAULT_THROTTLE_RATES":{
        #key为访问策略名,value为允许次数/周期(周期写法 s|m|h|d s代表1秒,m代表1分钟,h代表1小时,d代表1天)
        "aaa":"3/m" #频率:每分钟3次
    },
#}

对指定接口进行限流策略

# from .models import Book
# from rest_framework.views import APIView
# from rest_framework.response import Response
# from .serializers import BookSerializer
# from rest_framework.permissions import IsAuthenticated
from .throttle import MyThrottle        #导入框架的限流策略
# class BookView(APIView):
			throttle_classes = [MyThrottle]     #对这个接口 框架的限流策略

#     def get(self, request):
#         book_list = Book.objects.all()
#         ret = BookSerializer(book_list, many=True)
#         return Response(ret.data)

序列化器忽略

测试访问限流策略

访问接口第4次 被限流