Become a sponsor

请求限流
请求限流可通过中间件实现 IP 级别的限流,防止恶意请求和 DoS 攻击。项目中限流可通过 Nginx 反向代理或自定义中间件实现。
项目支持两种限流实现方式,可根据部署环境选择:
| 方式 | 适用场景 | 性能 | 精度 |
|---|---|---|---|
| Nginx 限流 | 生产环境(推荐) | 高(不占用应用进程) | 中 |
| Django 中间件 | 开发/测试环境 | 中(占用应用进程) | 高 |
Nginx 限流在反向代理层实现,不占用 Django 进程资源,适合生产环境:
# nginx.conf
http {
# 定义限流区域:按 IP 维度,每秒 10 个请求
# 10m 共享内存区域可存储约 16 万个 IP 地址
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
# 应用限流:突发允许 20 个请求排队,不延迟处理
limit_req zone=api_limit burst=20 nodelay;
# 限流时返回 429 状态码
limit_req_status 429;
# 限流日志级别
limit_req_log_level warn;
proxy_pass http://127.0.0.1:8000;
}
}
}| 参数 | 说明 | 示例 |
|---|---|---|
rate | 每秒允许的请求数 | 10r/s(每秒 10 个) |
burst | 突发请求数(排队等待) | burst=20 |
nodelay | 突发请求不延迟处理 | 有此参数则立即处理 |
limit_req_status | 限流时返回的状态码 | 429 |
limit_req_log_level | 限流日志级别 | warn |
http {
# API 接口限流:每秒 10 个请求
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 登录接口限流:每分钟 5 个请求(防暴力破解)
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
# 上传接口限流:每秒 2 个请求
limit_req_zone $binary_remote_addr zone=upload_limit:10m rate=2r/s;
server {
location /api/login {
limit_req zone=login_limit burst=3 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8000;
}
location /api/upload {
limit_req zone=upload_limit burst=5 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8000;
}
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:8000;
}
}
}Django 中间件限流在应用层实现,支持更灵活的限流规则:
# middleware/rate_limit_middleware.py
import time
from django.core.cache import cache
from django.http import JsonResponse
class RateLimitMiddleware:
"""基于缓存的 IP 限流中间件"""
def __init__(self, get_response):
self.get_response = get_response
self.rate_limit = 100 # 每分钟最大请求数
self.window = 60 # 窗口时间(秒)
def __call__(self, request):
# 获取客户端 IP
ip = self._get_client_ip(request)
cache_key = f'rate_limit:{ip}'
# 获取当前计数
current = cache.get(cache_key, 0)
if current >= self.rate_limit:
return JsonResponse({
'code': 1,
'msg': '请求过于频繁,请稍后再试',
'data': None,
'ok': False
}, status=429)
# 递增计数
cache.set(cache_key, current + 1, self.window)
response = self.get_response(request)
return response
def _get_client_ip(self, request):
"""获取客户端 IP,支持代理头"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
return request.META.get('REMOTE_ADDR')# application/settings.py
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'middleware.rate_limit_middleware.RateLimitMiddleware', # 限流中间件
'django.middleware.common.CommonMiddleware',
...
]class RateLimitMiddleware:
"""支持按接口差异化限流的中间件"""
# 接口限流配置
RATE_LIMITS = {
'/api/login': {'limit': 5, 'window': 60}, # 登录:每分钟 5 次
'/api/captcha': {'limit': 10, 'window': 60}, # 验证码:每分钟 10 次
'/api/upload': {'limit': 5, 'window': 60}, # 上传:每分钟 5 次
'default': {'limit': 100, 'window': 60}, # 默认:每分钟 100 次
}
def __call__(self, request):
ip = self._get_client_ip(request)
path = request.path
# 获取当前接口的限流配置
config = self.RATE_LIMITS.get(path, self.RATE_LIMITS['default'])
cache_key = f'rate_limit:{ip}:{path}'
current = cache.get(cache_key, 0)
if current >= config['limit']:
return JsonResponse({
'code': 1,
'msg': '请求过于频繁,请稍后再试',
'data': None,
'ok': False
}, status=429)
cache.set(cache_key, current + 1, config['window'])
return self.get_response(request)| 配置项 | 默认值 | 说明 |
|---|---|---|
rate_limit | 100 | 每分钟最大请求数 |
window | 60 | 统计窗口时长(秒) |
生产环境建议
生产环境建议使用 Nginx 限流,性能更好且不占用 Django 进程资源。Django 中间件限流适合开发环境和需要精细控制的场景。
当请求被限流时,返回 429 状态码:
{
"code": 1,
"msg": "请求过于频繁,请稍后再试",
"data": null,
"ok": false
}前端可通过 HTTP 状态码 429 识别限流,并提示用户稍后重试。
1. Redis 不可用:直接放行,不阻塞请求
2. 限流计数异常:降级放行,避免全站不可用
3. 限流阈值配置错误:使用默认值兜底fail-open 策略
限流采用 fail-open 策略:缓存不可用时放行所有请求,优先保证服务可用性。这是安全与可用性的权衡——宁可放过一些请求,也不能因为限流故障导致全站不可用。
验证码接口有独立的限流策略,按 IP 频率控制:
# 验证码防刷:单 IP 每分钟最多获取 5 次验证码
def check_captcha_rate_limit(ip):
cache_key = f'captcha_rate:{ip}'
count = cache.get(cache_key, 0)
if count >= 5:
return False # 超过限制
cache.set(cache_key, count + 60)
return True前端可通过 Axios 拦截器处理 429 响应:
// 响应拦截器
responseInterceptorsCatch: (error: any) => {
if (error.response?.status === 429) {
ElMessage.warning('请求过于频繁,请稍后再试');
return Promise.reject(error);
}
// ... 其他错误处理
}请求限流模块具备以下特点:
1. 多种实现:Nginx 限流(推荐)或 Django 中间件限流
2. IP 维度:按客户端 IP 限流,支持代理头
3. 滑动窗口:基于缓存的计数器实现
4. 降级策略:缓存故障时 fail-open 放行
5. 灵活配置:可调整限流阈值和窗口时间
6. 差异化限流:支持按接口独立配置限流策略
7. 验证码防刷:验证码接口有独立的频率控制