欢迎来到赛兔子家园

限流组件

限流,限制用户对某个接口访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。

  • 对于匿名用户,使用用户IP作为唯一标识;

  • 对于登录用户,使用用户ID或名称作为唯一标识;

  • 限流组件本质:循环每一个限流类实例对象,执行allow_request方法来判断是否限流;

使用步骤:

1、编写限流类

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache

MyThrottle(SimpleRateThrottle):
    cache = default_cache  # 访问记录存放在django的缓存中
    scope = "user"  # 构建缓存中的key
    # 设置访问频率,例如:1分钟允许访问10次
    # 其它:'s','sec','m','min','h','hour','d','day'
    THROTTLE_RATES = {"user": "5/m"}

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取用户IP
        return self.cache_format % {"scope": self.scope, "ident": ident}

2、django-redis配置-->settings.py

# redis配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",  # 安装redis的主机的 IP 和 端口
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 1000,
                "encoding": 'utf-8'
            },
            # "PASSWORD": "qwe123"  # redis密码
        }
    }
}

3、安装django-redid

pip3 install django-redis

4、启动redis服务

应用组件

局部应用(限流类定义在视图中)

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import SimpleRateThrottle
from rest_framework import exceptions
from rest_framework import status
from django.core.cache import cache as default_cache

from utils.view import MyAPIView

from utils.ext.per import ManagerPermission, BossPermission, UserPermission

class ThrottledException(exceptions.APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_code = 'throttled'


class MyThrottle(SimpleRateThrottle):
    cache = default_cache  # 访问记录存放在django的缓存中
    scope = "user"  # 构建缓存中的key
    cache_format = "throttle_%(scope)s_%(ident)s"

    # 设置访问频率,例如:1分钟允许访问10次
    # 其它:'s','sec','m','min','h','hour','d','day'
    THROTTLE_RATES = {"user": "5/m"}  

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取用户IP
        return self.cache_format % {"scope": self.scope, "ident": ident}

    def throttle_failure(self): # 自定义限流错误提示
        wait = self.wait()
        detail = {
            "code": 1005,
            "data": "访问频率",
            "detail": "需要等待{0}s才能访问".format(int(wait))
        }
        raise ThrottledException(detail)


class UserView(APIView):
    """无需登录接口 http://127.0.0.1:8000/api/auth/user/"""
    authentication_classes = []
    throttle_classes = [MyThrottle,]
    def get(self,request):
        return Response("用户信息")

全局应用(限流类定义在自定义throttle.py模块)

utils\ext\throttle.py

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache


class MyThrottle(SimpleRateThrottle):
    #scope = "XXX"   读取settings.py中全局配置
    #THROTTLE_RATES = {"XXX": "5/m"}
    cache = default_cache

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)
        return self.cache_format % {'scope': self.scope, 'ident': ident}

settings.py

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "ext.auth.QueryParamsAuthentication",
        "ext.auth.HeaderAuthentication",
        "ext.auth.NoAuthentication",
    ],
    "DEFAULT_THROTTLE_CLASSES":[
        "utils.ext.throttle.MyThrottle",
    ],
    "DEFAULT_THROTTLE_RATES":{
        "user":"10/m",
        "xx":"100/h"
    }
}

自定义限流类错误提示

默认超时提示:

{
    "detail": "请求超过了限速。 Expected available in 20 seconds."
}

throottle.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2024/3/19 21:20'
# software: PyCharm

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
from rest_framework import exceptions
from rest_framework import status

class ThrottledException(exceptions.APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_code = 'throttled'


class MyThrottle(SimpleRateThrottle):
    cache = default_cache  # 访问记录存放在django的缓存中
    scope = "user"  # 构建缓存中的key
    cache_format = "throttle_%(scope)s_%(ident)s"

    # 设置访问频率,例如:1分钟允许访问10次
    # 其它:'s','sec','m','min','h','hour','d','day'
    THROTTLE_RATES = {"user": "5/m"}

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取用户IP
        return self.cache_format % {"scope": self.scope, "ident": ident}

    def throttle_failure(self): # 自定义限流类错误提示
        wait = self.wait()
        detail = {
            "code": 1005,
            "data": "访问频率",
            "detail": "需要等待{0}s才能访问".format(int(wait))
        }
        raise ThrottledException(detail)

返回自定义错误提示

HTTP 429 Too Many Requests
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "code": "1005",
    "data": "访问频率",
    "detail": "需要等待55s才能访问"
}

多个限流类执行流程

本质,每个限流的类中都有一个 allow_request 方法,此方法内部可以有三种情况:

  • 返回True,表示当前限流类允许访问,继续执行后续的限流类。

  • 返回False,表示当前限流类不允许访问,继续执行后续的限流类。所有的限流类执行完毕后,读取所有不允许的限流,并计算还需等待的时间。

  • 抛出异常,表示当前限流类不允许访问,后续限流类不再执行。

posted on 2024-03-19 20:32  赛兔子  阅读(5)  评论(0编辑  收藏  举报

导航