flask默认是单线程阻塞模型,无法处理并发请求;开发可启用app.run(threaded=true),但生产必须用gunicorn或uwsgi部署。

Flask默认是单线程阻塞模型,不能靠app.run()扛真实请求
直接调用 app.run() 启动的 Flask 开发服务器,默认只用一个线程、一个进程,且不支持并发请求。哪怕只是两个浏览器标签同时刷新,第二个请求就会卡住,直到第一个返回——这不是“慢”,是彻底阻塞。生产环境绝对不能这么用。
常见错误现象:curl 或前端反复请求时,响应延迟陡增、超时、日志里看不到并行处理痕迹;用 time.sleep(5) 模拟耗时操作后,其他请求完全冻结。
- 开发调试阶段,可加参数启用多线程:
app.run(threaded=True)(仅限 Werkzeug 内置服务器,仍不适用于生产) - 生产部署必须换 WSGI 服务器:推荐
gunicorn(轻量、易配)或uWSGI(功能全、配置复杂) -
threaded=True不等于“高并发”,它只是让单个进程内跑多个线程,GIL 仍限制 CPU 密集型任务,并发能力有限
用gunicorn启动Flask时,worker类型和数量怎么选
gunicorn 的核心是 worker:每个 worker 是一个独立进程(或线程),负责接收和处理请求。选错类型或数量,要么压不起来,要么吃光内存。
使用场景:Web API、轻量后台服务、需稳定响应的内部系统。
- CPU 密集型任务(如图像处理、数值计算)→ 用
syncworker + 进程数 ≈ CPU 核心数(-w 4) - I/O 密集型任务(如调用数据库、HTTP 外部接口、文件读写)→ 用
geventworker(需pip install gevent),配合-k gevent -w 10 --worker-connections 1000 - 不要盲目堆
-w:每个 worker 默认占用 ~30–50MB 内存,16 个 worker 可能吃掉 800MB+,小内存机器会 OOM - 启动命令示例:
gunicorn -w 4 -b 0.0.0.0:5000 myapp:app(myapp:app是模块名:Flask 实例名)
异步任务别在请求中硬等,用threading或concurrent.futures解耦
有些逻辑必须耗时(比如发邮件、生成报表、调第三方慢接口),但又不想让用户干等。这时候不能在路由函数里 time.sleep() 或 requests.post() 同步等待,得把执行“甩出去”。
inference.sh 的 Python SDK:运行 AI 应用、构建智能体,并集成 150 多个模型。包名:inferencesh (pip install inferencesh)。支持同步/异步……
注意:Flask 路由函数本身仍是同步的,Python 的 async/await 对标准 Flask(非 Quart)无效。
- 简单后台任务:用
threading.Thread(daemon=True)启动,适合无状态、不需结果反馈的任务 - 需要控制生命周期或取返回值:用
concurrent.futures.ThreadPoolExecutor,配合submit()和回调 - 别在 thread 中直接访问 Flask 的
request或session:这些对象线程不安全,需提前提取必要参数传入 - 示例片段:
from concurrent.futures import ThreadPoolExecutor<br>executor = ThreadPoolExecutor(max_workers=4)<br><br>@app.route('/notify')<br>def send_notify():<br> user_id = request.args.get('user_id')<br> executor.submit(send_email_async, user_id) # 立即返回<br> return {'status': 'queued'}
数据库查询卡住?先确认是不是没设timeout或用了SELECT *
很多“Flask 卡死”实际是数据库连接被占满或查询没结束。Flask 本身不卡,是它等 DB 返回时被挂起。
典型表现:日志停在 SQL 执行前,ps aux | grep python 显示进程 CPU 为 0 但 RSS 持续上涨,netstat 查看 DB 连接长时间 ESTABLISHED。
- SQLAlchemy 用户:在
create_engine()里加connect_args={'timeout': 5}(SQLite)或pool_timeout=5(PostgreSQL/MySQL) - 避免在视图里做 N+1 查询:用
joinedload()或selectinload()预加载关联数据 - 禁止在循环里反复
db.session.query(...).first(),合并查或加缓存 - 用
EXPLAIN ANALYZE(PostgreSQL)或EXPLAIN(MySQL)看慢查询是否缺索引
真正棘手的是混合场景:比如一个请求既要查库、又要调外部 HTTP、还要写文件——这时候单靠改 worker 数量或开线程不够,得拆成明确的同步路径(快速返回)+ 异步管道(消息队列更稳)。但对多数中小项目,上面四点覆盖了 90% 的“卡死”根源。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










