DRF限流失效最常见原因是缓存未启用或配置错误,因SimpleRateThrottle及其子类强依赖Django缓存后端(如Redis或LocMemCache)存储计数器,不写数据库;若CACHES未配置、缺少'default'键,或使用不支持incr的后端,限流将静默退化为不限流。

DRF 的 Throttling 类开箱可用,但直接配置常因作用域理解偏差或缓存后端缺失而失效——它不依赖数据库,但强依赖 CACHES 配置,且限流键(get_cache_key)逻辑必须匹配真实请求上下文。
为什么加了 DEFAULT_THROTTLE_CLASSES 却没生效?
最常见原因是缓存未启用或配置错误。DRF 的 SimpleRateThrottle 及其子类全部基于 Django 缓存后端(如 LocMemCache 或 RedisCache)存储计数器,不会写数据库或文件。若 CACHES 未配置,或使用了不支持 incr 原子操作的后端(如某些 Memcached 封装),限流会静默退化为“不限流”。
- 检查
settings.py中是否定义了CACHES,至少要有'default'键;开发环境可临时用django.core.cache.backends.locmem.LocMemCache - 避免在测试时误配
CACHES = {}或漏掉'default'别名 - 使用 Redis 时确认
django-redis已安装,且BACKEND指向django_redis.cache.RedisCache
如何按用户、IP 或特定字段限流?
DRF 提供三类内置限流类:AnonRateThrottle(仅匿名用户)、UserRateThrottle(认证用户)、ScopedRateThrottle(按视图 throttle_scope 区分)。它们都继承自 SimpleRateThrottle,核心差异在于 get_cache_key() 返回的键名前缀不同:
-
AnonRateThrottle:键为"anon_<ip>"</ip>,基于request.META["REMOTE_ADDR"] -
UserRateThrottle:键为"user_<id>"</id>,要求用户已认证(request.user.is_authenticated为True) -
ScopedRateThrottle:需在视图中显式设throttle_scope = "upload",键为"scope_upload_<key>"</key>,其中<key></key>由get_cache_key决定(默认仍是 IP 或 user_id)
若需按请求参数(如 email 字段)限流,必须自定义类并重写 get_cache_key:
立即学习“Python免费学习笔记(深入)”;
class EmailRateThrottle(SimpleRateThrottle):
scope = 'email'
<pre class="brush:php;toolbar:false;">def get_cache_key(self, request, view):
email = request.data.get('email') or request.query_params.get('email')
if not email:
return None # 不限流
return f'email_{hashlib.md5(email.encode()).hexdigest()[:12]}'
rate 配置格式与实际触发时机
rate 是字符串,格式为 "X/Y",其中 X 是次数,Y 是时间单位(s、m、h、d)。注意:DRF 在每次请求进入 check_throttles() 时才校验,不是定时清理计数器。
-
"10/m"表示每分钟最多 10 次;若第 10 次发生在 12:00:59,第 11 次在 12:01:00,仍会拒绝——窗口是滑动的,非整点对齐 - 多个限流类同时启用时,任一触发即拒绝请求(逻辑是 AND,不是 OR)
- 调试时可在视图中临时加
print(self.get_throttles())确认实际生效的 throttle 实例
生产环境必须绕开的坑
LocMemCache 在多进程下无法共享计数器,UWSGI/Gunicorn 多 worker 时会导致限流完全失效。哪怕只用两个 worker,每个进程维护独立内存缓存,实际放行量会翻倍。
- 必须用 Redis 或 Memcached——哪怕单机也别图省事用
LocMemCache - 不要在
Throttle类中做耗时操作(如查 DB),get_cache_key应尽量轻量 -
UserRateThrottle对 JWT 认证用户可能失效:若认证逻辑在 DRFAPIView之外(如中间件中解析 token),request.user可能尚未设置,此时应改用自定义 throttle 并手动提取 user_id
限流键的设计和缓存一致性,比配置本身更关键。一个哈希错位的 get_cache_key 或一个没连上的 Redis,会让所有配置形同虚设。


















