restframework之节流

基本思路(原生Django而言):

  在django2.x中,若出现节流(访问频率控制)的需求,我们首先想到的是使用一个字典(dict类型)来存储所有IP地址的访问时间记录,这是针对于匿名用户(IP)而言的

{
    "111.222.3.44": [1000, 900, 700, 500, 10....],
    "1.333.44.555": [1200, 900, 20, 10.....]              
}

  原生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',
    },
}

  

原文地址:https://www.cnblogs.com/kisun168/p/11352998.html