应选用 flask-limiter 库,因其支持 Redis 分布式存储、自动原子计数、与 Flask 路由天然集成,避免手写限流导致的状态不同步、并发错误及维护困难问题。

Flask 限流该用哪个库?选 flask-limiter 而不是手写
直接上结论:别自己用 time.time() + 字典存请求时间,也别基于 session 或 cookie 做判断——状态难同步、不支持分布式、漏掉 API 场景。生产环境统一用 flask-limiter,它底层对接 Redis / Memcached / memory(开发可先用内存),自动处理键生成、过期、原子计数,且与 Flask 的 @app.route 和蓝图天然契合。
常见错误是把限流逻辑塞进视图函数里,导致每个请求都多一次 if 判断+手动计数,既难维护又容易在并发下计数错。而 flask-limiter 是 WSGI 中间层拦截,失败直接返回 429,不进视图。
- 安装:
pip install flask-limiter - 必须配存储后端:哪怕只本地测试,也建议起步就用
Redis(redis://localhost:6379),避免后期换存储时重写逻辑 - 注意初始化顺序:先创建
Limiter实例,再绑定到app,否则装饰器会报RuntimeError: limiter not bound
登录接口限流怎么配?按 IP + 路径组合键最稳妥
暴力破解的核心目标是 /login 这类无认证的 POST 接口,只按 IP 限会误伤 NAT 后用户,只按路径限又无法防单 IP 扫多个账号。正确做法是用动态键:IP + 请求路径 + (可选)用户名字段值。
示例中,对 POST /login 设置“每分钟最多 5 次”,键为 f"ip:{request.remote_addr}:login":
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
<p>app = Flask(<strong>name</strong>)
limiter = Limiter(app, key_func=lambda: f"ip:{get_remote_address()}:login", storage_uri="redis://localhost:6379")</p><p>@app.route("/login", methods=["POST"])
@limiter.limit("5 per minute")
def login():</p><h1>实际校验逻辑...</h1><pre class="brush:php;toolbar:false;">return jsonify({"status": "ok"})
- 别用
request.url当键——含 query string 会导致同一接口被拆成多个键,失去限流意义 - 如果想按用户名限(如防爆破某特定账号),可从
request.get_json()或request.form提取username,拼进键里,但注意空值或异常输入要 fallback 到 IP -
"5 per minute"是字符串 DSL,也支持"100 per day"或复合规则如"5 per minute and 100 per day"
429 响应怎么改得更友好?别依赖默认 HTML 页面
默认触发限流后返回的是纯文本 429 Too Many Requests,前端不好解析;更糟的是某些 Nginx 配置会拦截并替换成自己的错误页。必须主动接管响应格式。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
通过 limiter.exempt 和 limiter.request_filter 控制豁免逻辑,再用 @limiter.limit 的 on_failure 参数自定义返回:
def rate_limit_exceeded_handler():
return jsonify({
"error": "too_many_requests",
"retry_after": 60 # 单位秒,需和 limit 字符串匹配
}), 429
<p>limiter = Limiter(
app,
key_func=lambda: f"ip:{get_remote_address()}:login",
storage_uri="redis://localhost:6379",
on_failure=rate_limit_exceeded_handler
)
</p>
-
on_failure函数必须返回(response, status_code)元组,不能只 return dict - 务必加
Retry-After响应头(可在on_failure里用make_response设置),否则前端不知道该等多久 - 不要在
on_failure里调用数据库或发 HTTP 请求——它本身就在高负载路径上,任何阻塞都会放大雪崩风险
Redis 挂了限流还生效吗?memory 存储只是临时方案
用 storage_uri="memory://" 本地调试没问题,但上线必须切 Redis。因为 memory 存储是进程级的,gunicorn 多 worker 时各进程计数独立,实际限流失效;更严重的是进程重启后所有计数清零,等于没限。
Redis 故障时 flask-limiter 默认行为是「降级放行」——也就是不再限流,而非报错中断服务。这符合可用性优先原则,但你要清楚:这不是 bug,是设计。
- 检查 Redis 连接健康:启动时加
limiter._storage.check()(非公开 API,仅调试用),生产环境靠监控 Redis 的ping和info指标 - 不要给限流加 try/except 包裹——异常本就该抛出,让 Sentry 或日志系统捕获,而不是静默吞掉
- 如果业务对限流强依赖(如支付回调),需额外加一层应用级缓存(如本地 LRU)兜底,但会牺牲精度,慎用
真正难的是键设计和存储选型——这两处一旦线上跑起来,改起来牵一发而动全身。先跑通 Redis + IP+路径键,比纠结自定义算法重要得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










