DRF节流全攻略:为什么需要、如何设置、应用与自定义
在 nginx 反向代理中使用 limit_req 对入口进行“限流”无疑是有效的做法。但当你想为不同的视图/动作设置不同的策略(例如登录每分钟 5 次、上传每天 20 次、查询 API 每用户 1000 次)时,仅靠服务器/基础设施的配置往往难以满足需求。DRF 的节流功能让你可以在应用层根据“端点特性”创建对应的限制,成为必须掌握的基础技能。
为什么需要节流
DRF 节流像权限(permission)一样决定是否允许请求,但区别在于它是临时的(请求频率限制)而非永久的。官方文档将节流描述为“控制客户端向 API 发送请求速率的临时状态”。
现实需求大致可归纳为:
- 滥用/攻击缓解:暴力破解登录、垃圾请求、爬虫、简单 DoS 等
- 成本/资源保护:上传、外部 API 调用、重查询、生成式 AI 调用等“昂贵”端点
- 公平使用:防止单个用户/密钥占用资源
- 策略编码:将“每分钟 N 次”之类的规则放在代码中,而非基础设施
重要的是,节流既可以全局(global)也可以针对特定视图/动作(view/action)进行设置。仅靠 nginx 很难做到这一点。
DRF 节流的工作原理(核心概念)
1) Rate 字符串
DRF 通常使用类似 "100/day"、"60/min" 的字符串来定义限制。
2) “谁”被限制(客户端识别)
UserRateThrottle:已认证用户按 user id 限制,未认证用户按 IP 限制AnonRateThrottle:仅对未认证(anonymous)请求按 IP 限制ScopedRateThrottle:按 scope(如uploads)应用策略
IP 识别使用 X-Forwarded-For 或 REMOTE_ADDR,若处于代理后面,NUM_PROXIES 的设置尤为重要。
3) 状态存储使用缓存
默认实现将计数存储在 Django 缓存后端。单进程/单服务器可用 LocMemCache,但在多工作进程/多实例环境下,Redis 等共享缓存几乎是必需的。
全局(Global)设置:最快速的起步
settings.py:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/day",
"user": "1000/day",
},
}
这样即可为整个 API 应用默认策略。被限制的请求会得到 HTTP 429(Too Many Requests) 响应。
如何在视图中应用:按端点差异化限制
1) 基于类的视图(APIView)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
class ExpensiveView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request):
return Response({"ok": True})
2) 基于函数的视图(@api_view)
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle
from rest_framework.response import Response
@api_view(["GET"])
@throttle_classes([UserRateThrottle])
def ping(request):
return Response({"pong": True})
3) ViewSet 的特定 action(@action)
from rest_framework.decorators import action
from rest_framework.throttling import UserRateThrottle
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class ItemViewSet(ViewSet):
@action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle])
def purchase(self, request, pk=None):
return Response({"purchased": pk})
action 级别的节流优先于 ViewSet 级别的设置。
使用 ScopedRateThrottle 创建“按视图特性”策略(强烈推荐)
Scoped 节流让你可以用有意义的名称(scope)区分策略,管理更清晰。
settings.py:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.ScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
"uploads": "20/day",
"search": "60/min",
},
}
视图中仅声明 scope:
from rest_framework.views import APIView
from rest_framework.response import Response
class LoginView(APIView):
throttle_scope = "login"
def post(self, request):
return Response({"ok": True})
DRF 会为带 throttle_scope 的视图使用 ScopedRateThrottle,并以 scope + user id 或 IP 生成唯一键进行计数。
自定义节流:关键在于“如何获取键”
内置节流已能满足大多数需求,但实际业务中常见的自定义场景包括:
- 登录按 IP + username 组合限制
- 按 API Key 限制
- 按特定头/租户/组织单位限制
- 使用非默认缓存(如 Redis 集群)
1) 最常见的方式:继承 SimpleRateThrottle
只需重写 get_cache_key() 即可自定义限制依据。
from rest_framework.throttling import SimpleRateThrottle
class LoginBurstThrottle(SimpleRateThrottle):
scope = "login"
def get_cache_key(self, request, view):
username = (request.data.get("username") or "").lower().strip()
ident = self.get_ident(request) # IP 基础识别
if not username:
return None # 若无 username,则不限制
return f"throttle_login:{ident}:{username}"
在 settings.py 注册:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"path.to.LoginBurstThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
},
}
2) 更换缓存(cache attribute)
from django.core.cache import caches
from rest_framework.throttling import AnonRateThrottle
class CustomCacheAnonThrottle(AnonRateThrottle):
cache = caches["alternate"]
部署前必知
1) 代理环境下 IP 识别一致性
IP 识别基于 X-Forwarded-For/REMOTE_ADDR。若代理层不正确配置 NUM_PROXIES,所有用户会被视为同一 IP。
2) LocMemCache 在多进程/多服务器下不可靠
本地缓存会导致每个工作进程/服务器独立计数,节流失效。生产环境建议使用 Redis 等共享缓存。
3) 并发竞争条件(race condition)
DRF 内置实现可能在高并发下出现“多请求超额”的情况。若业务需要严格的 N 次限制(如支付/优惠券),建议使用原子计数(Redis INCR + EXPIRE)实现自定义节流。
4) 客户端友好性:429 与 Retry-After
DRF 默认返回 429。实现 wait() 方法即可在响应中加入 Retry-After 头,提示客户端何时可重试。
结语:nginx 与 DRF 节流,两者兼顾

- nginx:在最前端拦截大量流量/攻击,起到防御屏障
- DRF 节流:在应用层根据端点意义与成本,实施细粒度策略
尤其是“按视图特性差异化限制”,DRF 节流是最直观、最易维护的方案,且不受服务器环境变化影响。